Skip to main content
Glama
homeassistant-ai

Home Assistant MCP Server

Official

Breaking change (v7.3.0): ha_config_set_yaml has been moved to beta.

The Unofficial and Awesome Home Assistant MCP Server


Demo with Claude Desktop


🚀 Get Started

The recommended way to run ha-mcp is the HA-MCP Custom Component. It installs into Home Assistant through HACS, runs the full server in-process, and works on every Home Assistant installation type — Home Assistant OS, Supervised, Container, and Core — with full feature parity. It is the easiest setup in every case, with no access token to manage.

Add it to Home Assistant via HACS (the preferred install):

Add HA-MCP to HACS

Quick start:

  1. Install the HA-MCP Custom Component from HACS — click the badge above, or in HACS open Integrations → ⋮ → Custom repositories, add https://github.com/homeassistant-ai/ha-mcp-integration (category: Integration), then Download.

  2. Restart Home Assistant.

  3. Go to Settings → Devices & Services → Add Integration, search for HA-MCP Custom Component, choose HA-MCP Server, and click Submit. Creating the entry starts the server.

  4. Copy the connect URL from the entry's Configure screen (Settings → Devices & Services → HA-MCP Custom Component → HA-MCP Server → Configure) — it is also printed in the Home Assistant log. A notification confirms the server started and points you there.

  5. Paste that URL into your AI client — done.

Connect URL. The Configure screen gives you a Home Assistant webhook URL for remote clients — https://<your-ha-domain>/api/webhook/<webhook-id> through Nabu Casa or any reverse proxy already pointed at Home Assistant (locally, http://<ha-host>:8123/api/webhook/<webhook-id>). For clients on the same network, the server is also reachable directly at http://<ha-ip>:9584/private_<random>.

  • Replaces other install methods: the in-process server is a complete, standalone ha-mcp install — it takes the place of the app (add-on), Docker, and uvx/PyPI (stdio) methods. Run only one; do not run the in-process server alongside another install.

  • Local only? Turn off Remote access via webhook in the entry options — no webhook is registered at all, while the direct port and sidebar panel keep working.

  • Settings panel: while the server runs, an admin-only HA-MCP panel appears in the Home Assistant sidebar for managing tools, feature flags, backups, and themes.

  • Optional authentication: set Webhook authentication to ha_auth to require a Home Assistant account sign-in instead of using the secret URL as the credential.

  • Manual install (no HACS): copy custom_components/ha_mcp_tools/ from this repository into your Home Assistant config/custom_components/ directory, then restart and add the integration as above.

The component's second entry type, the File & YAML services entry (HA-MCP File & YAML Tools), is only needed if you enable ha-mcp's opt-in file and YAML editing tools (feature flags, off by default) — skip it otherwise; you can add it later at any time. It works with any server type (in-process, app, Docker, or stdio).

Full in-process server documentation → · Setup Wizard for client-specific config →

🏠 Home Assistant app (add-on)

Prefer to run ha-mcp as a Home Assistant app (add-on)? On Home Assistant OS and Supervised installs it is a close second — no access token to manage, and it works with Claude Desktop, Claude.ai, ChatGPT, and any other MCP client on your local network or configured for remote access.

  1. Add the repository to your Home Assistant instance:

    Add Repository

    If that opens the App store without an add-repository dialog (a known Home Assistant issue), add it manually: Settings → Apps → Install app → ⋮ → Repositories, then paste https://github.com/homeassistant-ai/ha-mcp.

  2. Install "Home Assistant MCP Server" from Settings → Apps → Install app and click Start. (Home Assistant 2026.2 renamed "Add-ons" to "Apps"; on older versions this is the Add-on store.)

  3. Open the Logs tab to find your unique MCP URL.

  4. Connect your AI client to that URL — no token or credential setup needed.

Full app documentation →

⚠️ Configure exactly one install method per client. The custom component, the app, Docker/PyPI, and local stdio are independent ways to run the same server — pick one and point your AI client at that single URL. Keeping two entries for the same server in one client (for example a local uvx ha-mcp@latest entry with HOMEASSISTANT_URL / HOMEASSISTANT_TOKEN alongside an app or component URL) is a known cause of connection hangs.

Other install methods

These run the server outside Home Assistant — useful for Container / Core installs (which can't run apps) or a separate host. The Setup Wizard generates the exact client-specific config for each.

  • Docker (HTTP server): run ghcr.io/homeassistant-ai/ha-mcp in HTTP mode, pointed at your Home Assistant URL and a long-lived token (an administrator's is recommended; non-admin tokens work with limitations), and connect your client to its secret URL. See the Setup Wizard for the full command and per-client config.

  • PyPI / uvx (HTTP server): run the published ha-mcp package with uvx ha-mcp@latest (or pip) as a streamable-HTTP server the same way. Details in the Setup Wizard.

  • Local stdio (not recommended): runs ha-mcp on your own machine over stdio. The one-command installers in the Demo server section below use this path; the Setup Wizard covers connecting it to your own Home Assistant.

  • OIDC authentication: gate remote access behind an external identity provider (Authentik, Keycloak, Auth0, etc.) instead of a secret URL — all authenticated users share the server's Home Assistant credentials. See OIDC Mode.

    ⚠️ stdio has known transport issues. The stdio transport has connection problems that streamable HTTP does not (#1713). It is recommended only for demo/testing tinkering — for a real setup, use the custom component or an HTTP method above.

Using the HA-MCP custom component? You do not need the Webhook Proxy — the component has its own built-in webhook for remote access (see the Get Started quick start at the top). The proxy is for the app (it can also front another external server via its mcp_server_url option). The OpenAI Tunnel below is different: it applies to any install method when Home Assistant isn't publicly reachable at all (no Nabu Casa or reverse proxy).

Already have Nabu Casa or another reverse proxy pointing at your Home Assistant? The Webhook Proxy app routes MCP traffic through your existing setup — no separate tunnel or port forwarding needed.

  1. Install the MCP Server app (see above) and the Webhook Proxy app from the same store

  2. Start the webhook proxy and restart Home Assistant when prompted

  3. Copy the webhook URL from the app logs:

    MCP Server URL (remote): https://xxxxx.ui.nabu.casa/api/webhook/mcp_xxxxxxxx
  4. Configure your AI client with that URL

For other remote access methods (Cloudflare Tunnel, custom reverse proxy), see the Setup Wizard.

ChatGPT / Codex behind a firewall (OpenAI Tunnel). ChatGPT connectors normally require a publicly reachable URL. If you can't (or don't want to) expose one, the community OpenAI Tunnel for HA-MCP integration by @norpol runs OpenAI's tunnel-client inside Home Assistant and connects your local MCP server URL to an OpenAI-hosted tunnel over an outbound-only connection — no port forwarding, reverse proxy, or public URL. Point it at your ha-mcp URL, then attach the ChatGPT connector to the same tunnel ID. See the FAQ entry and #1811.

Webhook proxy documentation →

🧪 Demo server (Windows / macOS / Linux)

Want to try ha-mcp before connecting your own Home Assistant? No paid subscription required. These one-command scripts set up a local stdio connection to a hosted demo environment so you can see it working in a few minutes. Each script's Connect your own Home Assistant link then shows how to point it at your instance.

  1. Go to claude.ai and sign in (or create a free account)

  2. Open Terminal and run:

    curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-macos.sh | sh
  3. Download Claude Desktop (or restart: Claude menu → Quit)

  4. Ask Claude: "Can you see my Home Assistant?"

You're now connected to the demo environment! Connect your own Home Assistant →

Anthropic doesn't ship Claude Desktop for Linux, so pick one path:

Claude Desktop — free, via the community build:

  1. Install the community Claude Desktop for Linux build and sign in with a free claude.ai account

  2. Open Terminal and run:

    curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-linux.sh | sh
  3. Restart Claude Desktop, then ask: "Can you see my Home Assistant?"

Claude Code — official CLI, requires a paid Claude plan:

  1. Install Claude Code: curl -fsSL https://claude.ai/install.sh | bash

  2. Configure ha-mcp, then run claude:

    curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install.sh | sh -s -- --claude-code
  3. Start claude, run /mcp to confirm, then ask: "Can you see my Home Assistant?"

Full Linux guide →

  1. Go to claude.ai and sign in (or create a free account)

  2. Open Windows PowerShell (from Start menu) and run:

    irm https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-windows.ps1 | iex
  3. Download Claude Desktop (or restart: File → Exit)

  4. Ask Claude: "Can you see my Home Assistant?"

You're now connected to the demo environment! Connect your own Home Assistant →

🧙 Setup Wizard for 15+ clients

Claude Code, Gemini CLI, ChatGPT, Open WebUI, VSCode, Cursor, and more.

Having issues? Check the FAQ & Troubleshooting


Related MCP server: hass-mcp-server

💬 What Can You Do With It?

Just talk to Claude naturally. Here are some real examples:

You Say

What Happens

"Create an automation that turns on the porch light at sunset"

Creates the automation with proper triggers and actions

"Add a weather card to my dashboard"

Updates your Lovelace dashboard with the new card

"The motion sensor automation isn't working, debug it"

Analyzes execution traces, identifies the issue, suggests fixes

"Make my morning routine automation also turn on the coffee maker"

Reads the existing automation, adds the new action, updates it

"Create a script that sets movie mode: dim lights, close blinds, turn on TV"

Creates a reusable script with the sequence of actions

Spend less time configuring, more time enjoying your smart home.


✨ Features

Category

Capabilities

🔍 Search

Fuzzy entity search, deep config search, system overview

🏠 Control

Any service, bulk device control, real-time states

🔧 Manage

Automations, scripts, helpers, dashboards, areas, zones, groups, calendars, blueprints

📊 Monitor

History, statistics, camera snapshots, automation traces, ZHA devices

💾 System

Backup/restore, updates, apps, device registry

🔒 Safety

Read Only Mode toggle, per-tool enable/disable, tool security policies (user approval), automatic edit backups

Template helper edit backups capture persisted options through the HA-MCP custom component, using either its Server entry or File & YAML Tools entry. Helper edits, generic integration options edits, and deletion capture the stable config-entry identity and entity ID/name mapping. Capture refuses degraded secret scrubbing. Restoring an existing helper requires a fresh safety backup, removes optional settings absent from the snapshot, and verifies persisted options.

For Template backup listing and bulk deletion, filter by the config-entry ID returned as entity_id by capture. Capture accepts an entity alias; the history filters use the stable config-entry ID. Recreation reports its replacement config-entry ID and saved entity mapping separately.

If the original entry has been deleted, restore recreates the helper and reports its new config-entry ID. The saved entity ID and custom name are restored when available; an occupied entity ID is refused before creation. Older snapshots without entity metadata cannot preserve renamed entity IDs. If a collision or verification failure occurs after creation, the new helper and source backup are retained and the result reports the new entry for inspection. Other entity/device registry settings are not restored. Helper and integration edits through the same server wait until restore finishes; other Home Assistant clients are outside this coordination. Failed or uncertain restores report their outcome and safe refusal reasons; inspect the current helper before retrying.

Category

Tools

Apps (add-ons)

ha_get_app, ha_manage_app

Areas & Floors

ha_list_floors_areas, ha_remove_area_or_floor, ha_set_area_or_floor

Assist

ha_manage_pipeline

Automations

ha_config_get_automation, ha_config_remove_automation, ha_config_set_automation

Blueprints

ha_manage_blueprints

Calendar

ha_config_get_calendar_events, ha_config_remove_calendar_event, ha_config_set_calendar_event

Camera

ha_get_camera_image

Dashboard

ha_get_dashboard_screenshot (beta)

Dashboards

ha_config_delete_dashboard_resource, ha_config_delete_dashboard, ha_config_get_dashboard, ha_config_list_dashboard_resources, ha_config_set_dashboard_resource, ha_config_set_dashboard

Developer

ha_dev_manage_server, ha_dev_manage_settings

Device Registry

ha_get_device, ha_remove_device, ha_set_device

Energy

ha_manage_energy_prefs

Entity Registry

ha_get_entity_exposure, ha_get_entity, ha_remove_entity, ha_set_entity

Files

ha_delete_file (beta), ha_list_files (beta), ha_read_file (beta), ha_write_file (beta)

Groups

ha_config_list_groups, ha_config_remove_group, ha_config_set_group

HACS

ha_get_hacs_info, ha_manage_hacs

Helper Entities

ha_config_list_helpers, ha_config_set_helper, ha_remove_helpers_integrations

History & Statistics

ha_get_automation_traces, ha_get_history, ha_get_logs

Integrations

ha_get_integration, ha_get_system_health, ha_set_integration

Labels & Categories

ha_config_get_category, ha_config_get_label, ha_config_remove_category, ha_config_remove_label, ha_config_set_category, ha_config_set_label

Matter

ha_manage_radio

Scenes

ha_config_get_scene, ha_config_remove_scene, ha_config_set_scene

Scripts

ha_config_get_script, ha_config_remove_script, ha_config_set_script

Search & Discovery

ha_get_overview, ha_get_state, ha_search

Service & Device Control

ha_bulk_control, ha_call_event, ha_call_service, ha_get_operation_status, ha_list_services

System

ha_config_get_yaml (beta), ha_config_set_yaml (beta), ha_manage_backup, ha_manage_custom_tool (beta), ha_manage_security_policy, ha_manage_theme, ha_manage_updates, ha_reload_core, ha_restart

Todo Lists

ha_get_todo, ha_remove_todo_item, ha_set_todo_item

Utilities

ha_eval_template, ha_report_issue

Zones

ha_get_zone, ha_remove_zone, ha_set_zone

Z-Wave configuration values can be read through ha_manage_radio with action="get_config_params" or action="get_config_param", even when their configuration entities are disabled or absent. Reads use the Z-Wave JS cache by default; a single full root parameter can explicitly request a device read. See parameter reads, freshness, and examples.


🆚 ha-mcp vs. Home Assistant's built-in MCP Server

Home Assistant ships its own MCP Server integration. It is built on the Assist pipeline, so a connected MCP client can read and control the entities you have exposed to Assist and run the intents Assist understands — handy for voice-style control of already-exposed devices.

ha-mcp is a standalone server built for configuring, building, and debugging your smart home, not just controlling it. On top of device control, it adds capabilities the built-in integration does not have:

Capability

Built-in MCP Server

ha-mcp

Control exposed devices, query states

Yes

Yes

Entity scope

Only entities exposed to Assist

Everything in Home Assistant

Create / edit automations, scripts, scenes

No

Yes

Build & edit dashboards

No

Yes

Debug automations from traces, read history & logs

No

Yes

Manage helpers, areas, zones, labels, groups

No

Yes

Backups, apps, HACS, device & entity registry

No

Yes

Rule of thumb: Use the built-in integration for voice-style control of devices you have already exposed; use ha-mcp when you want an AI assistant that can also build and maintain your Home Assistant setup.


🔌 Custom Component (ha_mcp_tools) — File & YAML Services

The HA-MCP Custom Component also powers a set of privileged tools that standard Home Assistant APIs can't provide: file system access and YAML config editing. (The same component runs the full server in-process — that's the recommended install in the Get Started section at the top.) Its File & YAML services entry (HA-MCP File & YAML Tools) enables the tools below.

Tools that require the component:

Tool

Description

ha_config_set_yaml (beta)

Safely add, replace, or remove top-level YAML keys in configuration.yaml and package files (automatic backup, validation, and config check)

ha_list_files (beta)

List files in allowed directories

ha_read_file (beta)

Read files from allowed paths (config YAML, logs, and allowed directories)

ha_write_file (beta)

Write files to allowed directories

ha_delete_file (beta)

Delete files from allowed directories

Template helper edit backups and restores also require the component, using either its Server entry or File & YAML Tools entry. These five tools return an error with installation instructions if the component is missing.

These tools also require beta feature flags. See Beta Features for how to enable them — including the ENABLE_BETA_FEATURES master flag, which must be on before the filesystem/YAML sub-flags take effect.

Install

Install the HA-MCP File & YAML Tools entry from the same HA-MCP Custom Component:

Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.

To add manually: open HACS > Integrations > three-dot menu > Custom repositories > add https://github.com/homeassistant-ai/ha-mcp-integration (category: Integration) > Download. Or copy custom_components/ha_mcp_tools/ from this repository into your HA config/custom_components/ directory.

After installing, restart Home Assistant, then open Settings > Devices & Services > Add Integration, search for HA-MCP Custom Component, and add the HA-MCP File & YAML Tools entry.

To run the full ha-mcp server in-process through this same component, see the Get Started section at the top and the full in-process server documentation →.


🧠 Better Results with Agent Skills

This server gives your AI agent tools to control Home Assistant. For better configurations, pair it with Home Assistant Agent Skills — domain knowledge that teaches the agent Home Assistant best practices.

An MCP server can create automations, helpers, and dashboards, but it has no opinion on how to structure them. Without domain knowledge, agents tend to over-rely on templates, pick the wrong helper type, or produce automations that are hard to maintain. The skills fill that gap: native constructs over Jinja2 workarounds, correct helper selection, safe refactoring workflows, and proper use of automation modes.

Bundled Skills (built-in)

Skills from homeassistant-ai/skills are bundled and served as MCP resources via skill:// URIs. Any MCP client that supports resources can discover them automatically — no manual installation needed. For tool-only clients (claude.ai, etc.), the best-practices skill is reachable through the ha_get_skill_guide tool: call it with no arguments to read SKILL.md, or with file set to a path SKILL.md links to read that reference file. Resources are not auto-injected into context — clients must explicitly request them, so idle context cost is just the metadata listing.

ha_get_skill_guide is a mandatory tool: the catalog always exposes it (it can't be disabled) so tool-only clients never see a silently missing skill surface.

Skills can still be installed manually for clients that prefer local skill files — see the skills repo for instructions.


🔍 Tool Discovery for AI Agents

By default, the full tool catalog (~87 tools) is listed to the client through the standard MCP tools/list response. Clients with deferred / on-demand tool loading (claude.ai, Claude Desktop, Claude Code) handle that fine — tools are pulled into context only when needed, so idle context cost is near-zero.

For setups without deferred tool support — models like Claude Haiku, Gemini, OpenAI-compatible local models and smaller open-weights models, or clients that inline all tool schemas regardless of model (e.g. GitHub Copilot CLI) — listing the full tool catalog up front adds a lot of idle context and can overwhelm smaller models. To address that, the server ships with a search-based discovery mode built on top of FastMCP's BM25 search transform.

Smaller or local LLMs (Ollama, etc.)

If your model can't see the tools or your Home Assistant, it may be getting handed the whole tool catalog at once and struggling with it. It's recommended to try the following to see if it helps:

  • Enable tool search (ENABLE_TOOL_SEARCH=true, or the app option below). Instead of listing every tool up front, the server defers the catalog behind a search interface so the model pulls in only the tools it needs, when it needs them.

  • Raise the model's context window above the default. Local runtimes ship with small defaults (Ollama's num_ctx is one example) that can't hold a large tool set plus the conversation — increase it well beyond the default.

Enable search-based discovery

Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA app). The full catalog is replaced in the tool list with four entry points plus a small set of always-visible "pinned" tools (ha_search, ha_get_overview, ha_report_issue, etc.). All tools remain callable directly by name once discovered:

Tool

Purpose

ha_search_tools

BM25 English-keyword search across all tools, pinned ones included. Hidden tools return name, description, parameters, and annotations (readOnlyHint / destructiveHint) so the agent can pick the right one; a pinned tool returns a name-only stub (pinned: true) pointing back at the tool list, and does not use up a result slot.

ha_call_read_tool

Execute a readOnlyHint tool by name. Safe — clients can auto-approve.

ha_call_write_tool

Execute a write tool that creates or updates data.

ha_call_delete_tool

Execute a tool that removes / deletes data.

The proxy split lets MCP clients apply different permission policies per category (e.g. auto-approve reads, prompt for writes, confirm deletes) without parsing tool docstrings.

A ha_manage_* tool combines several operations, so it is reachable from more than one proxy: ha_call_write_tool and ha_call_delete_tool run the whole tool, while ha_call_read_tool runs only the read actions Read Only Mode approves (the same per-call verdict), so a manage tool with no approved read actions is not reachable there. Search results list each proxy the tool can be reached through. In Read Only Mode only ha_call_read_tool is listed; the other two still answer a client that holds them in a cached tool list, and any write they carry gets the Read Only Mode error rather than Unknown tool.

Setting

Default

Description

ENABLE_TOOL_SEARCH

false

Replace full tool catalog with search-based discovery (tools deferred behind on-demand search).

TOOL_SEARCH_MAX_RESULTS

5

Max hidden tools returned per ha_search_tools call (range 2–10); a pinned tool that ranks inside that top count is added as a name-only stub on top of it.

PINNED_TOOLS

empty

Comma-separated tool names to keep always visible. The web settings UI is the primary way to manage this.

When to enable

  • Claude Haiku, OpenAI-compatible local models, Gemini, or any model without native deferred tool support — large idle-context savings. The same applies to clients that inline all tool schemas regardless of model (e.g. GitHub Copilot CLI, even when running Claude Sonnet/Opus).

  • MCP clients that cap total tool count (some cap at 100) — surfaces a minimal set (~10 tools) instead of 87.

  • Cost-sensitive deployments — fewer idle tokens per turn.

Leave it off in clients with deferred tool loading (claude.ai, Claude Desktop, Claude Code); the full catalog has no idle cost there, direct calls skip the search step, and the client's built-in tool search is the better choice — there is no benefit to running ha-mcp's on top of it. Whether tools are deferred depends on the client and model combination: the same model can behave differently per client — GitHub Copilot CLI running Claude Sonnet/Opus inlines the full catalog and still benefits from tool search here. Some Codex models and ChatGPT include deferred tools too — check your client/model directly to confirm its features so you don't leave this enabled unnecessarily.

🔄 Refresh your client's tool list after changing this (or any) setting. Toggling ENABLE_TOOL_SEARCH (or changing pinned/disabled tools, Read Only Mode, etc.) changes the tools the server exposes, but your AI client keeps serving its cached tool list until it re-fetches. Restarting the app or Home Assistant does not refresh the client — reconnect or refresh the MCP server in your client (e.g. re-add/refresh the connector in ChatGPT, or close and reopen Claude Desktop). If you skip this, newly enabled tools won't appear in the client at all, and tools the server no longer exposes still show as available but fail when called (Unknown tool, or the Read Only Mode error for a write tool that mode hides). ChatGPT sometimes keeps serving the stale list even after the connector is removed and re-added under the same name — if tools are still missing after re-adding, delete the connector and create a new one with a different name.

For the HA app, the same option is documented in homeassistant-addon/DOCS.md along with the in-app settings UI for fine-grained tool enable/disable/pin.


Read-only HTTP connections

Append /readonly to the server's HTTP MCP endpoint to restrict that connection to the existing Read Only Mode while other clients keep normal access:

Normal:     https://example.com/private_your_secret
Read-only:  https://example.com/private_your_secret/readonly

OAuth and OIDC connections use the same login/provider: for example, https://example.com/mcp/readonly. No additional secret or server is required. Read-only connections hide write tools and block write calls, including calls through cached tools or search proxies. The global Read Only Mode setting still restricts both endpoints when enabled. Reconnect the client after changing its URL so it refreshes its tool list.

This is a connection mode for automated agents, not a separate permission on the credential: the same credentials still work at the normal endpoint. Home Assistant webhook URLs also accept the suffix: https://your-ha.example/api/webhook/<webhook-id>/readonly. This requires the updated embedded integration or Webhook Proxy dev app (add-on), together with the updated MCP server.

🧪 Dev Channel

Want early access to new features and fixes? Dev releases (.devN) are published on every push to master.

Dev Channel Documentation — Instructions for pip/uvx, Docker, and Home Assistant app.


🤝 Contributing

For development setup, testing instructions, and contribution guidelines, see CONTRIBUTING.md.

For comprehensive testing documentation, see tests/README.md.


🔒 Privacy

Ha-mcp runs locally on your machine. Your smart home data stays on your network.

  • No telemetry today — anonymous usage stats are a planned future feature (as of June 2026); when it lands it will follow your Home Assistant analytics/telemetry setting (which you can override), announced prominently in the release notes and the web Settings UI at least one month beforehand

  • No personal data collection — we never collect entity names, configs, or device data

  • User-controlled bug reports — only sent with your explicit approval

For full details, see our Privacy Policy.


📄 License

This project is licensed under the MIT License - see the LICENSE file for details.


🙏 Acknowledgments

  • Home Assistant: Amazing smart home platform (!)

  • FastMCP: Excellent MCP server framework

  • Model Context Protocol: Standardized AI-application communication

  • Claude Code: AI-powered coding assistant

  • PolicyLayer: Argument-path predicate DSL shape (args.domain in [...] with eq/in/regex/contains/exists/...) inspired the per-tool approval rule schema (#966).

👥 Contributors

Maintainers

Contributors

  • @bigeric08 — Explicit mcp dependency for protocol version 2025-11-25 support.

  • @airlabno — Support for data field in schedule time blocks.

  • @ryphez — Codex Desktop UI MCP quick setup guide.

  • @Danm72 — Entity registry tools (ha_set_entity, ha_get_entity) for managing entity properties.

  • @Raygooo — SOCKS proxy support.

  • @cj-elevate — Integration & entity management tools (enable/disable/delete); person/zone/tag config store routing.

  • @maxperron — Beta testing.

  • @kingbear2 — Windows UV setup guide.

  • @konradwalsh — Financial support via GitHub Sponsors. Thank you! ☕

  • @knowald — Area resolution via device registry in ha_get_system_overview for entities assigned through their parent device. Financial support via GitHub Sponsors. Thank you! ☕

  • @zorrobyte — Per-client WebSocket credentials in OAuth mode, fixing WebSocket tool failures.

  • @deanbenson — Fixed ha_deep_search timeout on large Home Assistant instances with many automations.

  • @saphid — Config entry options flow tools (initial design, #590).

  • @adraguidev — Fix menu-based config entry flows for group helpers (#647).

  • @transportrefer — Integration options inspection (ha_get_integration schema support, #689).

  • @teh-hippo — Fix blueprint import missing save step.

  • @smenzer — Documentation fix.

  • @The-Greg-O — REST API for config entry deletion.

  • @restriction — Responsible disclosure: python_transform sandbox missing call target validation.

  • @lcrostarosa — Diagnostic and health monitoring tools concept (#675), inspiring system/error logs, repairs, and ZHA radio metrics integration.

  • @roysha1 — Copilot CLI support in the installation wizard; replaced placeholder logo SVGs with real brand icons on the documentation site.

  • @teancom — Fix add-on stats endpoint (/addons/{slug}/stats).

  • @TomasDJo — Category support for automations, scripts, and scenes.

  • @bzelch — python_transform support for automations and scripts.

  • @gcormier — Windows installer improvements: removed unused variable and fixed terminal closing after install.

  • @ekobres — Feature flags for HAMCP_ENABLE_FILESYSTEM_TOOLS and the (since removed) HAMCP_ENABLE_CUSTOM_COMPONENT_INTEGRATION in the app config, with beta tagging in source and docs.

  • @w3z315 — Financial support via GitHub Sponsors. Thank you! ☕

  • @griffinmartin — Added OpenCode (by Anomaly) as a selectable AI client in the setup wizard, with both stdio and streamable HTTP support.

  • @hhopke — Fixed app (add-on) API calls to route through HA Core ingress proxy instead of direct container connections, fixing ha_manage_addon (now ha_manage_app) proxy mode on app installs.

  • @tomwilkie — JMESPath middleware exploration (#1147) whose review-time token-measurement data informed the design of #1199 and #1225.

  • @SealKan — fields=/attribute_keys= projection on six read-heavy tools (#1225), ha_call_event tool (#1239), dashboards-list helper refactor (#1207), for:-field duration-math detector in the best-practice checker (#1264), persistent DCR OAuth client registrations across restarts (#1265), and issue-triage prompt token-budgeting (#1522).

  • @KarelTestSpecial — Cached YAML instance to prevent CPU spikes during bulk edits (#1371).

  • @corgan2222 — HA brand assets for custom integration (#1317).

  • @drseanwing — Progress emission via FastMCP Context in long-running tools (#1124); tool-discovery / categorized-search docs (#1123).

  • @fnordpig — Config subentry support (#1393) and Assist pipeline management tool (#1392).

  • @paul43210 — array_patch mode in ha_manage_app for atomic GET-modify-POST (#1063).

  • @L1AD — Filed #966 proposing tool security policies; pointed to PolicyLayer's MCP-security work as prior art that inspired the predicate DSL shape.

  • @nightcityblade — Updated stale Home Assistant Advanced Mode references after HA 2026.6 made formerly advanced options available by default (#1533).

  • @emmelutzer — Financial support via GitHub Sponsors. Thank you! ☕

  • @pkkr — ha_knx_get_project tool exposing KNX group addresses from an uploaded ETS project file.

  • @cbowns — Fixed inconsistent hyphen in setup.astro Codex CLI docs.

  • @Shaan-alpha — Extended ha_restart known-good error patterns to cover 502/503 responses from reverse proxies.

  • @rebelancap — Fixed UTC-to-local timezone conversion in add_timezone_metadata.

  • @saevras — Fixed blueprint import E2E test to use local URL instead of host-to-container networking.

  • @jasonjhofmann — Recurring calendar events via rrule support in ha_config_set_calendar_event.

  • @vpciii — Coerce JSON-encoded strings on dict/list tool params.

  • @pburtchaell — Financial support via GitHub Sponsors. Thank you! ☕

  • @norpol — Built the OpenAI Tunnel for HA-MCP companion integration, connecting ChatGPT to a firewalled Home Assistant MCP server (#1811).


💬 Community

  • GitHub Discussions — Ask questions, share ideas

  • Issue Tracker — Report bugs, request features, or suggest tool behavior improvements. Bug reports need the report the ha_report_issue tool generates, or a reason why there is none; without either they are closed after 24 hours.


Star History

Available Tools

77 tools
ha_bulk_controlBulk ControlA
Destructive

Manage explicit operations or one deterministic structural bulk action.

When NOT to use: use ha_call_service for service-specific payloads or backend-native group targeting, and ha_search for fuzzy name discovery.

Operations mode (operations, no selector): put every target in this one call. Parallel execution is the default, and invalid items are reported without aborting valid operations in the same batch — but a batch in which every item fails validation dispatches nothing and fails the call. A batch that targets a group/aggregate entity together with one or more of its own individual members also fails closed (nothing dispatched): Home Assistant applies the action to every member when the group is targeted regardless of what else is listed, so a member row cannot exclude that member from the group's own action. Use selector mode with exclude_entity_ids when a group action must exclude specific members.

Selector mode (selector + action): use exact area or floor IDs when exclusions must be applied after recursively expanding generic aggregate membership. Resolves a frozen visible leaf set before dispatch; it is not transactional, so Home Assistant may still report per-leaf failures. A selector resolving to more than 100 entities fails closed instead of dispatching a partial/oversized batch — narrow it (a more specific area/floor, or add exclude_entity_ids) and retry. Set dry_run to preview the resolved set without changing state.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionNoOne device action applied to every resolved leaf.
dry_runNoSelector mode only; True is rejected in operations mode.
parallelNoDispatch operations concurrently (default) or one at a time when False.
selectorNoOptional exact structural scope using domain plus area_ids and/or floor_ids, with optional exclude_entity_ids.
operationsNoExplicit entity operations. Each item requires exact entity_id and action. Use action='off', not service='turn_off'.
parametersNoOptional action parameters for selector mode.
validate_firstNoSelector mode only: report ENTITY_NOT_FOUND for a target that does not exist. In operations mode set validate_first on each operation instead; a top-level False is rejected in operations mode.
timeout_secondsNoSelector mode only: confirmation wait in seconds for every resolved entity. In operations mode set timeout_seconds on each operation instead; a top-level value is rejected in operations mode.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare destructiveHint=true and openWorldHint=false, so the description carries most of the burden and does so richly: parallel-by-default dispatch, partial failures reported without aborting, all-invalid batches fail closed, group+own-member batches fail closed, non-transactional selector dispatch, and a >100-entity fail-closed cap. These are non-obvious behaviors an agent could not infer from annotations or schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The response is long but front-loads purpose and mode selection with bold headers, so it is easy to navigate. A few passages are verbose — notably the trailing Claude Desktop timeout/approval advice, which is operationally useful but reads as troubleshooting rather than tool semantics.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 8-parameter, two-mode destructive tool with an output schema, the description covers mode selection, failure semantics, and constraints well enough to call correctly. Return values are correctly left to the output schema, though the per-domain parameter allowlist is only partially addressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it explains that selector exclusions apply after recursive membership expansion, that a top-level validate_first/timeout_seconds is rejected in operations mode, and the 'off' vs 'turn_off' action naming convention. It goes beyond restating the schema without fully specifying per-domain parameter allowlists.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names two specific operating modes ('operations mode' and 'selector mode') keyed to explicit parameters, and explicitly distinguishes itself from ha_call_service and ha_search. An agent can tell immediately what the tool does and which sibling to use instead.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides an explicit 'When NOT to use' block naming the two alternatives and the conditions that select them, plus in-mode guidance (use selector mode with exclude_entity_ids when a group action must exclude members). Both the when and the when-not are stated, not inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_call_eventCall EventA
Destructive

Execute a custom event on the Home Assistant event bus.

When NOT to use: for controlling entities (lights, switches, climate) — use ha_call_service instead. To run an automation now, use ha_config_set_automation(identifier=..., run_actions=True).

Use this to publish custom event types consumed by event-triggered automations, Node-RED flows, or custom integrations that subscribe to specific event types.

Caveats: Events are fire-and-forget; this tool confirms the event was accepted by the bus but does not verify whether any automation or subscriber acted on it.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
dataNoOptional event payload, delivered to subscribers as the event's data (trigger.event.data in an automation).
event_typeYesEvent type to fire, e.g. 'my_custom_event'.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint, idempotentHint=false, and openWorldHint=false, so the safety profile is covered structurally. The description adds real value beyond them: fire-and-forget semantics, that acceptance by the bus does not confirm subscriber action, and client-timeout troubleshooting. It does not reconcile the destructiveHint=true annotation, leaving one small gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads purpose, then usage, then caveats in a logical order. The final Claude Desktop timeout/manual-approve paragraph is operationally useful but somewhat niche and verbose relative to the core definition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists and annotations carry the safety profile, so the description need not explain return values. It covers purpose, alternatives, and fire-and-forget caveats comprehensively, with only the destructiveHint annotation left unexplained.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both event_type and data are already documented, including the trigger.event.data delivery detail. The description offers no additional syntax, format, or example beyond what the schema provides, making the baseline 3 correct.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Execute a custom event on the Home Assistant event bus') and immediately scopes it against siblings by naming ha_call_service and ha_config_set_automation. An agent can distinguish this from entity control and automation running without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit 'When NOT to use' guidance with named alternatives and the exact condition that routes to each (entity control vs. running automations). It also positively describes the intended use case: event types consumed by event-triggered automations, Node-RED, or custom integrations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_call_serviceCall ServiceA
Destructive

Call any Home Assistant service or one-shot WebSocket command: the catch-all escape hatch.

When NOT to use: a dedicated tool that covers the job. It validates the request, reports the outcome in its own terms, and stays available where a security policy gates this tool. For example: turn an automation on or off or run it now with ha_config_set_automation (enabled / run_actions); start or stop a script with ha_config_set_script (run); activate a scene with ha_config_set_scene (activate); manage apps (add-ons) with ha_manage_app; handle updates and Repairs issues with ha_manage_updates; set an integration's log level with ha_set_integration (log_level).

When to use: a service no dedicated tool covers. Services follow the pattern domain.service (e.g., light.turn_on, climate.set_temperature); ha_list_services lists them with their fields.

EXAMPLES:

  • ha_call_service("light", "turn_on", entity_id="light.living_room")

  • ha_call_service("climate", "set_temperature", entity_id="climate.thermostat", data={"temperature": 22})

Result compaction (default ON): result is trimmed to the targeted entity's record (drops parent-group propagation) and stripped of context / last_* metadata and heavy attribute lists (effect_list, hue_scenes).

For detailed service documentation, use ha_get_skill_guide.

WebSocket command escape hatch (advanced): Some Home Assistant operations are WebSocket-only commands, not registered services. Pass ws_command (instead of domain/service) to send one no dedicated tool covers, with its parameters in data: ha_call_service(ws_command="", data={...}). Only one-shot request/response commands are supported, and the other service parameters (entity_id, return_response, etc.) don't apply.

Unavailable in Read Only Mode, including read-like services and WebSocket commands. Use dedicated read tools while that mode is enabled.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
dataNoExtra service-call parameters beyond entity_id (e.g. {'temperature': 22} for climate.set_temperature). Also carries the raw command payload when ws_command is set. If entity_id is also present in data, the entity_id parameter wins.
waitNoIf True (default), wait for the entity state to change before returning. Applies only to state-changing services called with a single entity_id. A comma-separated multi-target does not get confirmed by this: it falls through to a legacy path that polls for the literal composite entity_id and times out after 10s. Set wait=False for multi-target calls.
domainNoService domain (e.g. 'light', 'climate', 'automation'). Required for a service call.
serviceNoService name within domain (e.g. 'turn_on', 'set_temperature', 'trigger'). Required for a service call.
verboseNoReturn HA's raw changed-state records unchanged. With return_response=True the response data still surfaces once as the top-level service_response key, never nested in result. Large for nested-group targets — prefer result_fields / result_attribute_keys.
entity_idNoEntity ID(s) the service call targets — one ID ('light.living_room') or several comma-separated ('light.a,light.b'). Optional for services that don't target a specific entity.
ws_commandNoAdvanced escape hatch: send a raw one-shot Home Assistant WebSocket command that is NOT a registered service and that no dedicated tool covers. When set, omit domain/service and the other service params. Streaming/two-phase and service-invoking commands (call_service, execute_script) are rejected.
result_fieldsNoProject each record in 'result' to only these top-level keys (e.g. ['entity_id', 'state']). Setting this DISABLES default compaction — no entity-id filter, no metadata strip — and applies the explicit projection instead.
return_responseNoIf True, the service's response data is returned once, as the top-level 'service_response' key — never nested inside 'result'.
result_attribute_keysNoProject each record's 'attributes' dict to only these keys (e.g. ['brightness', 'rgb_color']). Setting this DISABLES default compaction. Requires 'attributes' to be present in result_fields (or result_fields=None).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructive=true and readOnly=false, but the description adds real operational context beyond them: default-on result compaction (what gets dropped), unavailability in Read Only Mode, the multi-target wait/timeout caveat, WS-command restrictions, and the 4-minute timeout guidance. This is behavioral disclosure the schema and annotations do not carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Long, but well-structured and front-loaded with the escape-hatch framing and the critical when-not-to-use routing. The timeout troubleshooting paragraph is useful but sits at the end and reads as somewhat incidental; overall it earns its length without much waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return-value explanation is optional, yet the description still clarifies result compaction, service_response key behavior, and verbose mode. Combined with the routing and mode-restriction notes, an agent has everything needed to call this correctly in context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so parameters are already documented, but the description still adds value: domain.service format, that ws_command is mutually exclusive with the other service params, the entity_id-wins-over-data precedence note, and result compaction interactions with result_fields. Slightly redundant with the schema in places, hence not a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (call) plus resource (Home Assistant service / one-shot WebSocket command) and frames itself explicitly as 'the catch-all escape hatch.' It distinguishes itself from siblings by naming which dedicated tools cover which jobs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Unusually strong routing: a 'When NOT to use' section names concrete alternatives (ha_config_set_automation, ha_config_set_script, ha_config_set_scene, ha_manage_app, ha_manage_updates, ha_set_integration) with the specific parameters that replace this call, followed by a 'When to use' condition and two worked examples.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_delete_dashboardDelete DashboardA
Destructive

Delete a storage-mode dashboard completely.

WARNING: This permanently deletes the dashboard and all its configuration. Cannot be undone. Does not work on YAML-mode dashboards.

HA internal IDs may differ from url_path (e.g. hyphens → underscores); the tool resolves either form to the actual registry ID before deletion.

EXAMPLES:

  • Delete dashboard: ha_config_delete_dashboard("mobile-dashboard")

Note: The default dashboard cannot be deleted via this method.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
url_pathYesDashboard URL path or internal ID to delete (e.g., 'my-dashboard' or 'my_dashboard'). Both forms are accepted.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the destructiveHint annotation by disclosing irreversibility, scope of deletion (config included), the YAML-mode exclusion, and the default-dashboard restriction. It even adds an operational troubleshooting note about the 4-minute timeout and approval flow that an agent could not infer from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and the WARNING, which is good, but it then sprawls into an example, a second note, and a long Claude-Desktop-specific timeout paragraph that is only relevant to a narrow client. The timeout paragraph in particular dilutes an otherwise tight definition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers the destructive contract, the preconditions (storage-mode only, non-default), the ID-resolution behavior, and client-specific failure modes. With an output schema present, no return-format discussion is needed, so the description is complete for a destructive single-param tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so the schema fully documents url_path including the dual-form acceptance. The description's ID-resolution note ('hyphens → underscores') adds a bit of context about how either form is handled, but this is marginal gain over the schema baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Delete') and resource ('storage-mode dashboard') and scopes the operation precisely. It explicitly distinguishes itself from YAML-mode dashboards and adjacent sibling tools like ha_config_delete_dashboard_resource, so the agent can tell them apart.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when it does not apply (YAML-mode dashboards, default dashboard) and warns about permanence. However it never names the sibling to use instead (e.g., a setter or resource-deleter) as a positive alternative, so it stops short of a full routing 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_delete_dashboard_resourceDelete Dashboard ResourceA
Destructive

Delete a dashboard resource.

Removes a resource from Home Assistant. The resource will no longer be loaded on dashboards.

WARNING: Deleting a resource used by custom cards in your dashboards will cause those cards to fail to load.

EXAMPLES: ha_config_delete_dashboard_resource(resource_id="abc123")

Note: Use ha_config_list_dashboard_resources() to find resource IDs before deleting. Ensure no dashboards depend on the resource.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
resource_idYesResource ID to delete.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, but the description adds real consequence detail beyond them: the resource is no longer loaded on dashboards, and custom cards depending on it will fail to load. It also discloses retry/timeout behavior ('read the target back before repeating it'). The operational note is useful, though partially tool-agnostic noise.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core sentences and the example are well front-loaded and earn their place, but the final paragraph about a Claude Desktop 4-minute timeout and manual-approve buttons is verbose, tool-agnostic, and dilutes the definition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter destructive tool with an output schema present, the description covers action, precondition, consequence, and recovery guidance. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Single parameter with 100% schema coverage, so the schema already documents resource_id fully. The example call shows the expected ID format, which adds marginal value, but the baseline of 3 applies when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Delete a dashboard resource') and clearly distinguishes itself from the sibling ha_config_list_dashboard_resources and ha_config_set_dashboard_resource. An agent can identify it without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent: 'Use ha_config_list_dashboard_resources() to find resource IDs before deleting. Ensure no dashboards depend on the resource.' This names the alternative tool and the precondition for safe use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_get_automationGet Automation ConfigA
Read-onlyIdempotent

Get Home Assistant automation configuration.

Returns the complete configuration including triggers, conditions, actions, and mode settings.

The returned config_hash stays the same across consecutive reads of an unchanged config.

The returned automation_id is the resolved entity_id (canonical form, e.g. automation.morning_routine) when the registry lookup succeeds, falling back to the input identifier otherwise.

EXAMPLES:

  • Get automation: ha_config_get_automation("automation.morning_routine")

  • Get by unique_id: ha_config_get_automation("my_unique_automation_id")

For comprehensive automation documentation, use ha_get_skill_guide.

read inspect fetch view existing automation config triggers conditions actions get show detail

ParametersJSON Schema
NameRequiredDescriptionDefault
identifierYesAutomation entity_id (e.g., 'automation.morning_routine') or unique_id

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover readOnly/idempotent/openWorld, so the bar is lower; the description adds genuinely useful behavior: config_hash stability across unchanged reads and the automation_id resolution/fallback rule. It does not mention permissions or failure modes for a missing automation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose followed by return-shape notes and examples; every paragraph carries information. The trailing keyword string ('read inspect fetch view...') is low-value filler, keeping it from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be documented, yet the description usefully explains the two opaque return fields (config_hash, automation_id) and gives invocation examples. Complete enough for a single-parameter read tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline is 3, and the description goes beyond it by clarifying that the identifier may be either an entity_id or a unique_id and explaining that the returned automation_id is the resolved canonical entity_id with a fallback to the input. That adds semantic value the schema does not.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Get Home Assistant automation configuration') and enumerates the payload (triggers, conditions, actions, mode settings). It is trivially distinguishable from siblings like ha_config_set_automation and ha_config_remove_automation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Two concrete invocation examples cover both identifier forms and it routes the agent to ha_get_skill_guide for documentation, which is clear positive guidance. It stops short of stating when-not-to-use (e.g. vs ha_get_state for live state rather than stored config).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_get_calendar_eventsGet Calendar EventsA
Read-onlyIdempotent

Get calendar events from a calendar entity within a time range.

Returns each event's summary, start, end, description and location, plus the uid, recurrence_id and rrule when the calendar provides them (the edit and remove tools use uid and recurrence_id). To find calendar entities, use ha_search(domain_filter='calendar').

EXAMPLES:

  • Next week (defaults): ha_config_get_calendar_events("calendar.family")

  • Date range: ha_config_get_calendar_events("calendar.work", start="2024-01-01T00:00:00", end="2024-01-31T23:59:59")

ParametersJSON Schema
NameRequiredDescriptionDefault
endNoEnd datetime in ISO format (default: 7 days from now, not from start; pass end whenever you pass start)
startNoStart datetime in ISO format (default: now)
entity_idYesCalendar entity ID (e.g., 'calendar.family')
max_resultsNoMaximum number of events to return

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly=true, idempotent=true, openWorld=false, so the safety profile is covered. The description adds meaningful context beyond that: the cross-tool relationship that uid and recurrence_id feed the edit and remove tools, which is not derivable from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose sentence, then output contents, then routing hint, then two compact examples. Each section earns its place and nothing is padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, annotations covering the safety profile, and 100% schema description coverage, the description supplies everything else an agent needs: purpose, entity discovery path, default window behavior, and cross-tool linkage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The worked examples add real value: they demonstrate that calling with only entity_id yields the default (next week) window and show the ISO start/end pairing, making the default temporal behavior concrete rather than just implied.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Get), resource (calendar events), and scope (from a calendar entity within a time range). It distinguishes itself from the sibling edit/remove calendar tools by naming them, so an agent can tell which calendar tool to pick without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent to ha_search(domain_filter='calendar') for finding entities and notes that the edit/remove tools consume the returned uid/recurrence_id. There is no explicit when-not guidance, but the context for use is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_get_categoryGet CategoryA
Read-onlyIdempotent

Get category info - list all categories for a scope or get a specific one by ID.

Without a category_id: Lists all Home Assistant categories for the given scope. With a category_id: Returns configuration for that specific category.

Categories are domain-scoped organizational groups for automations, scripts, scenes, and helpers.

EXAMPLES:

  • List helper categories: ha_config_get_category("helpers")

  • Get specific category: ha_config_get_category("automation", category_id="my_category_id")

Use ha_config_set_category() to create or update categories. Use ha_set_entity(categories={"automation": "category_id"}) to assign categories to entities.

ParametersJSON Schema
NameRequiredDescriptionDefault
scopeYesDomain scope for categories (e.g., 'automation', 'script', 'scene', 'helpers').
category_idNoID of the category to retrieve.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and openWorldHint=false, so the safety profile is covered. The description adds parameter-dependent behavior (list all without category_id, retrieve one with it) and contextualizes categories as domain-scoped organizational groups, which is useful beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core behavior, then uses short paragraphs and examples to clarify edge cases and alternatives. Every sentence contributes useful information without repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low complexity, rich annotations, complete schema, and presence of an output schema, the description covers all that an agent needs: scope behavior, conditional semantics, examples, and relevant sibling alternatives. No critical information is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema itself documents both parameters. The description adds clarifying semantics by explaining that omitting category_id changes the operation from retrieval to listing, and provides concrete examples for scope values, going beyond the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (get) and resource (category), and explicitly distinguishes listing all categories from retrieving one by ID. It also names sibling tools for creation/update and assignment, so an agent can tell it apart without opening other schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit conditional usage: omit category_id to list, include it to retrieve a specific category. It also names the alternatives ha_config_set_category() for create/update and ha_set_entity(categories=...) for assignment, covering when to use this tool versus others.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_get_dashboardGet DashboardA
Read-onlyIdempotent

Get dashboard info - list all dashboards, get config, or search for cards.

MODE 1 — List: list_only=True Lists every dashboard's metadata (url_path, title, icon), storage and YAML alike (metadata only — bodies are never included here).

MODE 2 — Search: any of entity_id / card_type / heading provided Finds cards, badges, and header cards matching the criteria, including cards nested inside stacks, grids, conditional cards, button-card custom_fields, and state-switch states. Each match carries a python_path and a jq_path that locate the card for nested as well as top-level cards. The python_path is a Python subscript chain to be appended after config — e.g. python_transform=f'config{m["python_path"]}["icon"] = "mdi:x"' (it is NOT valid on its own without the config prefix). jq_path is the same location in jq dot-notation. Multiple criteria are AND-ed. Always fetches fresh config, bypassing the cache. Search covers cards/card/custom_fields/states containers up to a depth bound; if the dashboard carries a non-traversed child-bearing shape (e.g. picture-elements elements), the result carries a warnings entry naming where, so its hidden content is not mistaken for absent. Strategy dashboards are not searchable (no explicit cards).

MODE 3 — Get: Active when list_only=False and no search parameters are provided. Returns the full Lovelace dashboard config, defaulting to the main dashboard if url_path is omitted. With view_path, config_hash still covers the FULL config, so a follow-up ha_config_set_dashboard(python_transform=...) addressing config['views'][view_index] validates unchanged. An unknown view_path errors and lists the available view paths. When you only need the render and not the config, use the dedicated ha_get_dashboard_screenshot tool (registered when the dashboard screenshot beta feature is on) instead; it returns images without echoing the config.

MODE 4 — Search all: mode="search" with query= Answers "which dashboards contain this entity/card" by walking every storage-mode dashboard's views/cards/sections for the query substring. Each match names the url_path, view, card_path, card_type, and the matched field/value. Takes precedence over the other modes (list_only / entity_id / card_type / heading are ignored when mode="search"). YAML-mode dashboards are never searched on either path — the component walk skips them in-process and the component-less legacy walk skips any row tagged mode="yaml" — because HA resolves !secret when loading a YAML Lovelace config, so searching one could surface resolved secrets. On installs without the ha_mcp_tools component, the default (unnamed) dashboard is also not searched — only dashboards with a url_path are.

MODE 2 (Search) and MODE 3 (Get) return a config_hash that stays the same across consecutive reads of an unchanged config; MODE 1 and MODE 4 do not return one.

EXAMPLES:

  • List all dashboards: ha_config_get_dashboard(list_only=True)

  • Get one view only: ha_config_get_dashboard(url_path="lovelace-mobile", view_path="office")

  • Find cards by entity (wildcards allowed): ha_config_get_dashboard(url_path="my-dash", entity_id="sensor.temperature_*")

  • Find heading: ha_config_get_dashboard(url_path="my-dash", heading="Climate", card_type="heading")

  • Which dashboards use an entity: ha_config_get_dashboard(mode="search", query="light.bedroom")

SEARCH WORKFLOW EXAMPLE:

  1. find = ha_config_get_dashboard(url_path="my-dash", entity_id="light.bedroom")

  2. ha_config_set_dashboard( url_path="my-dash", config_hash=find["config_hash"], python_transform=f'config{find["matches"][0]["python_path"]}["icon"] = "mdi:lamp"' )

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNoSet to 'search' (requires query).
queryNoWith mode='search': the entity_id or substring to find across all storage-mode dashboards. Ignored otherwise.
headingNoFind cards by heading/title text (case-insensitive partial match).
url_pathNoDashboard URL path (e.g., 'lovelace-home'). Use 'default' for default dashboard.
card_typeNoFind cards by type, e.g. 'tile', 'button', 'heading'.
entity_idNoFind cards by entity ID. Supports wildcards, e.g. 'sensor.temperature_*'. Matches cards with this entity in 'entity' or 'entities' field, view-level badges, and header cards.
list_onlyNoWhen True, url_path is ignored.
view_pathNoGet mode: return ONLY the view whose Lovelace views[].path matches (response carries 'view' + 'view_index' instead of the full 'config'). With include_screenshot, also selects the view to render. Ignored in list/search mode.
force_reloadNoForce reload from storage (bypass cache). Not applicable in search mode.
include_configNoIn search mode: include each matched card's own configuration object in results. A container card's body includes its descendants, which are also separate matches with their own bodies, so nested stacks multiply the payload. Bodies are returned only for dashboards provably in storage mode; for a YAML or unconfirmed dashboard they are withheld (they may carry resolved !secret values) and the response says so, with match locations still reported. Ignored outside search mode.
include_screenshotNoGet mode only: also return rendered image(s) of the dashboard for visual verification. Requires the 'dashboard screenshot' beta feature + engine app (add-on)/sidecar. If the feature is disabled the config is returned with a warning; if the engine is configured but the render fails, the call errors. Ignored in list/search mode.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/closed-world, but the description goes well beyond them: cache bypass behavior, config_hash stability per mode, the secret-exposure risk that excludes YAML dashboards from search, the depth bound and `warnings` entry for non-traversed shapes, and the fact that strategy dashboards are unsearchable. This is exactly the additional behavioral context annotations cannot carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the mode taxonomy and organized into labeled MODE sections plus examples, so it scans well despite its length. A few clauses (the nested python_transform aside, the search-all secret rationale) are verbose, but nearly every sentence carries actionable information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 11-parameter, multi-mode read tool with no output schema, the description covers return shape (url_path/title/icon, config_hash, view/view_index, matches with python_path/jq_path, warnings) and mode interactions thoroughly. Nothing needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds cross-parameter semantics the schema does not: criteria are AND-ed, python_path must be prefixed with `config`, jq_path is the jq equivalent, and mode overrides the other mode-selecting params. It stops short of 5 only because most per-parameter detail is already in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb+resource ('Get dashboard info') and then decomposes it into four precisely named modes (list, search, get, search-all), each with its own trigger condition. An agent can distinguish this from ha_config_set_dashboard, ha_config_delete_dashboard, and ha_get_dashboard_screenshot without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use for every mode, including precedence ('mode="search" takes precedence; list_only/entity_id/card_type/heading are ignored'), an explicit alternative tool for a narrower need (ha_get_dashboard_screenshot), and a worked search→set workflow. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_get_labelGet LabelA
Read-onlyIdempotent

Get label info - list all labels or get a specific one by ID.

Without a label_id: Lists all Home Assistant labels with their configurations. With a label_id: Returns configuration for that specific label.

EXAMPLES:

  • List all labels: ha_config_get_label()

  • Get specific label: ha_config_get_label("my_label_id")

Use ha_config_set_label() to create or update labels. Use ha_set_entity(labels=["label1", "label2"]) to assign labels to entities, ha_set_device(labels=[...]) for devices, or ha_set_area_or_floor(kind="area", labels=[...]) for areas.

ParametersJSON Schema
NameRequiredDescriptionDefault
label_idNoID of the label to retrieve.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover readOnlyHint, idempotentHint and openWorldHint, so safety is established. The description adds the dual list/get behavior and concrete invocation examples, but does not mention failure behavior for an unknown label_id, which is a minor remaining gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core behavior, then examples, then related-tool routing. The assignment-tool paragraph is somewhat expansive but earns its place by preventing wrong-tool selection.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists and annotations declare the safety profile, so the description only needs to explain invocation modes and routing – which it does completely. An agent has everything needed to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema documents label_id, so the baseline would be 3. The description goes beyond it by defining the semantic meaning of omitting the parameter (list all) versus providing it (single lookup), which materially helps invocation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get label info') and disambiguates its two modes: list all labels or retrieve one by ID. It also names sibling set/assign tools, so an agent can place it immediately in the label toolset.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states the two calling conditions (without label_id = list, with label_id = specific) with worked examples, and routes adjacent tasks to ha_config_set_label and ha_set_entity/device/area_or_floor. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_get_sceneGet or Find ScenesA
Read-onlyIdempotent

Get a scene's complete configuration, or list and search scenes without scene_id.

Use ha_search for cross-domain discovery and dependency searches. For ordinary scene discovery, use this tool and pass a returned scene_id back to retrieve the complete entities dict and config_hash for editing (pass that hash to ha_config_set_scene for python_transform updates).

Listing returns compact metadata. Integration-managed scenes have no editable storage config or scene_id. Partial content-search results explicitly report unread configs and are not exhaustive.

EXAMPLES:

  • Get scene: ha_config_get_scene("movie_night")

  • Find scenes: ha_config_get_scene(query="movie")

  • Find attribute values: ha_config_get_scene(query="rainbow", search_in_config=True)

For detailed scene configuration help, use ha_get_skill_guide.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum scenes per page
queryNoFilter scene names or IDs
offsetNoPagination offset
scene_idNoScene storage ID from listing (e.g. 'movie_night') or the scene's entity_id; an entity_id is resolved to the storage ID.
search_in_configNoAlso search full stored scene attribute values within a bounded scan

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/openWorld=false, yet the description adds real behavioral context: listing returns compact metadata only, integration-managed scenes have no editable storage config or scene_id, and content-search results are explicitly non-exhaustive because unread configs are reported. This exceeds what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the two modes, then routing guidance, then behavioral caveats, then example calls. Every sentence carries a distinct constraint or routing rule with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be spelled out, and the description still names the returned entities dict and config_hash needed for the edit workflow. Combined with the safety annotations and full schema coverage, an agent has everything required to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description nonetheless adds meaning by clarifying the two accepted forms of scene_id (storage ID vs entity_id resolution), the intended use of search_in_config for attribute values, and the non-exhaustive nature of that search. Examples map parameters to concrete calls.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific dual-mode purpose (get a scene's full config, or list/search scenes without scene_id) and explicitly differentiates itself from ha_search and from the write path ha_config_set_scene. An agent can tell this apart from siblings like ha_config_remove_scene or ha_search without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes: use ha_search for cross-domain/dependency discovery, use this tool for ordinary scene discovery, and pass the returned scene_id back plus the config_hash to ha_config_set_scene for edits. It also qualifies when search_in_config applies and notes that listing metadata is compact.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_get_scriptGet Script ConfigA
Read-onlyIdempotent

Get Home Assistant script configuration.

Returns the complete configuration for a script, including sequence, mode, fields, and other settings.

The returned config_hash stays the same across consecutive reads of an unchanged config.

The returned script_id is the canonical bare storage key resolved by the REST client (matching what ha_config_set_script / ha_config_remove_script expect), falling back to the input identifier on the rare path where the REST envelope omits it. Prefix handling matches ha_config_get_automation in behavior (mechanism differs: automations resolve via state lookup; scripts strip the prefix).

EXAMPLES:

  • Get script (bare form): ha_config_get_script("morning_routine")

  • Get script (entity_id form): ha_config_get_script("script.morning_routine")

For detailed script configuration help, use ha_get_skill_guide.

read inspect fetch view existing script config sequence actions get show detail

ParametersJSON Schema
NameRequiredDescriptionDefault
script_idYesScript identifier — bare storage key ('morning_routine') or entity_id form ('script.morning_routine'); a leading 'script.' prefix is stripped before lookup.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover readOnly/idempotent/openWorld, yet the description adds genuinely new behavioral facts: config_hash stability across consecutive reads, the canonical script_id resolution and its rare fallback, and the prefix-stripping mechanism contrasted with automations' state lookup. This is well beyond what the structured fields disclose.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core explanation is front-loaded and useful, but the prefix-handling paragraph is somewhat over-elaborate and the trailing keyword string ('read inspect fetch view existing script config sequence actions get show detail') is pure keyword stuffing that adds no meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-shape documentation is not required, yet the description still clarifies the two salient returned identifiers (config_hash, script_id). For a single-parameter read tool with full schema coverage and annotations, nothing needed for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3; the description adds value by clarifying that a leading 'script.' prefix is stripped before lookup and that the bare key matches what set/remove expect. The examples reinforce both accepted forms, though no additional format or edge-case semantics beyond this.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Get) and resource (Home Assistant script configuration) and enumerates the returned content (sequence, mode, fields). It is clearly distinguishable from the sibling write/remove tools ha_config_set_script and ha_config_remove_script, and it explicitly positions itself against ha_config_get_automation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives two concrete invocation examples (bare form and entity_id form) and routes users to ha_get_skill_guide for deeper configuration help. It never states an explicit when-not-to-use or a precondition like 'read before calling set_script', so it falls short of the 5 benchmark.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_list_dashboard_resourcesList Dashboard ResourcesA
Read-onlyIdempotent

List Lovelace dashboard resources (custom cards, themes, CSS/JS).

Returns one page of registered resources; total_count and has_more report the full set, and inline_count and by_type summarise every resource, not just this page. Each resource has a unique ID for update/delete operations. For inline resources (created with ha_config_set_dashboard_resource(content=...)), shows a preview of the content instead of the full encoded URL to save tokens.

EXAMPLES:

  • Next page: ha_config_list_dashboard_resources(offset=100)

  • List with full content: ha_config_list_dashboard_resources(include_content=True)

Home Assistant 2026.6+ exposes resource management in the UI by default; API access works regardless of UI availability.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax resources to return per page
offsetNoNumber of resources to skip for pagination
include_contentNoInclude full decoded content for inline resources in the "_content" field. When False, a 150-char preview is shown instead. Rows past the per-response content budget carry '_content_truncated': True instead of '_content'; request a smaller page before copying content into an update.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/openWorld, so the description earns credit for adding behavior: total_count/has_more/inline_count/by_type describe the FULL set rather than just the page, and inline resources return a token-saving preview instead of the full encoded URL. That last detail materially affects what an agent sees and does.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the purpose, then pagination semantics, then examples in a clearly labeled block. Slightly longer than strictly necessary, particularly the Home Assistant 2026.6+ UI note, but every section carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists, yet the description still clarifies the tricky parts an agent would misread: summary counts span all resources, and content is previewed or truncated under budget. Combined with the read-only annotations, nothing needed to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description goes beyond by supplying worked examples with actual values (offset=100, include_content=True) and by framing include_content as a token tradeoff rather than just a boolean flag.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List Lovelace dashboard resources') and immediately scopes it to custom cards, themes, CSS/JS. Sibling tools like ha_config_set_dashboard_resource and ha_config_delete_dashboard_resource make this the read-side counterpart, and the description confirms that role by noting each resource carries an ID for update/delete.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides concrete invocation examples for the two non-default behaviors (offset=100 to page, include_content=True for full content) and explains when to care about inline previews. It stops short of explicitly naming when to prefer a sibling tool, but the read-only listing scope is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_list_groupsList GroupsA
Read-onlyIdempotent

List Home Assistant entity groups with their member entities.

Returns one page of old-style groups created via the group.set service or YAML configuration (platform-specific groups such as light or cover groups are separate entities); total_count and has_more report the full set. Each group includes its entity_id, friendly name, state, member entities, icon and all mode.

EXAMPLE: ha_config_list_groups(offset=100)

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax groups to return per page
offsetNoNumber of groups to skip for pagination

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint=false, so safety is covered. The description adds real behavioral context beyond that: results are paginated, total_count/has_more describe the full unpaginated set, and the scope is limited to legacy group types. It stops short of describing error behavior or ordering.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose, then scope, pagination semantics, and an example call. No redundant sentences; every clause adds information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter paginated read with an output schema present, the description covers purpose, scope, pagination behavior and even the returned field set. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so limit and offset are already documented; the baseline is 3. The example call with offset=100 illustrates pagination in practice but adds no meaning beyond the schema's own parameter descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List Home Assistant entity groups with their member entities') and immediately scopes the domain: old-style groups from group.set/YAML, explicitly excluding platform-specific groups such as light or cover groups. An agent can tell this apart from set/remove group siblings and from helper/area listings without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the tool's nature (read-only listing) and the note that total_count/has_more cover the full set, which hints at pagination strategy. However, no explicit when-to-use, when-not-to-use, or alternative (e.g., ha_config_get_group, ha_config_list_helpers) is named, so the agent must infer routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_list_helpersList HelpersA
Read-onlyIdempotent

List Home Assistant helpers of a specific type with their configurations.

Returns one page of helpers; total_count and has_more report the full set. Each record carries the complete configuration for its helper: id (immutable storage key), entity_id (current — address the helper by this, where available), name (current display name), original_name (creation-time name), icon, type-specific settings, and area and label assignments.

For a helper renamed in the UI, id/original_name keep the storage values while entity_id/name reflect the current entity registry (entity_id is the identifier ha_config_set_helper resolves against, so prefer it over id for a renamed helper). entity_id/original_name are present only for storage-collection helpers matched in the entity registry — types with no backing entity (e.g. tag), and every record when the registry read degrades, carry only id/name (a warning flags the degraded case).

Storage types list what HA's {type}/list command returns: the storage-backed helpers (created via UI/API), not the YAML-defined ones. person is the exception — HA lists its YAML-configured persons alongside the storage ones, so both appear here.

Flow-based types (template / group / utility_meter / derivative / etc.) require the ha_mcp_tools custom component (>= 1.1.0) and are served only through it. Requesting a flow type without the component returns a COMPONENT_NOT_INSTALLED error.

With helper_type="all", each record carries its own helper_type. This mode is component-only (there is no single built-in command that lists all types): without the ha_mcp_tools component it returns a COMPONENT_NOT_INSTALLED error rather than a partial or empty list.

EXAMPLES:

  • List all counters: ha_config_list_helpers("counter")

  • List every helper type at once: ha_config_list_helpers("all")

  • Next page: ha_config_list_helpers("input_boolean", offset=100)

For detailed helper documentation, use ha_get_skill_guide.

list all helpers input_boolean input_number input_text counter timer input_datetime input_select

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax helpers to return per page
offsetNoNumber of helpers to skip for pagination
helper_typeYesHelper type to list. Storage types are listed on all installs; flow-based types require the ha_mcp_tools custom component. Pass 'all' to list every helper type in one call (also requires the ha_mcp_tools component).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish read-only/idempotent safety, so the description's job is to add context — and it delivers: pagination semantics (total_count/has_more), the degraded-registry warning case, the COMPONENT_NOT_INSTALLED error paths, and the person YAML exception. This is exactly the kind of behavior an agent needs before calling.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose and record shape, then layered detail in digestible paragraphs. Slightly long, and the trailing keyword string ('list all helpers input_boolean ...') is search-stuffing that doesn't earn its place, holding it back from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an output schema exists, return-value explanation isn't required, yet the description still covers the fields an agent will act on (identity resolution, degraded case) plus all error conditions. For a paginated list tool with two type families, this is complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline is 3, but the description adds real meaning beyond the schema: it explains the id vs entity_id vs original_name semantics under renaming, and clarifies that 'all' is component-only rather than a partial fallback. That goes beyond what the enum list conveys.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List Home Assistant helpers of a specific type with their configurations') and immediately scopes it against siblings by noting what it is not (YAML-defined helpers, non-storage types). An agent can separate this from ha_config_set_helper and ha_config_list_groups without reading schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides concrete usage examples (single type, 'all', pagination) and routes the agent to ha_get_skill_guide for deeper docs. It also states conditions that change behavior (flow types need the custom component). It stops short of an explicit when-not-to-use statement versus sibling list tools, so not a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_remove_automationRemove AutomationA
DestructiveIdempotent

Delete a Home Assistant automation permanently.

The returned automation_id is the resolved entity_id (canonical form, e.g. automation.morning_routine) when the registry lookup succeeded before the delete, falling back to the input identifier otherwise.

EXAMPLES:

  • Delete automation: ha_config_remove_automation("automation.old_automation")

  • Delete by unique_id: ha_config_remove_automation("my_unique_id")

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
waitNoWait for automation to be fully removed before returning.
identifierYesAutomation entity_id (e.g., 'automation.old_automation') or unique_id to delete

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, so safety framing is covered; the description adds genuine value by stating the deletion is permanent and by documenting the return-value fallback (resolved entity_id vs input identifier) plus a concrete timeout-recovery procedure. The main gap is that it never says the operation is idempotent or what happens on a second call.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core action, then examples, then troubleshooting. The Claude Desktop timeout paragraph is verbose and tool-support specific, but it is last and practically actionable, so it earns most of its space.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the return-value discussion is partly redundant, yet the resolved-vs-fallback behavior is useful. Annotations cover the destructive/idempotent profile. The description is nearly complete for a 2-parameter destructive tool; only repeated-invocation behavior is unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and both parameters are already described (identifier accepts entity_id or unique_id; wait controls synchronous removal), so the description's examples largely restate the schema. Baseline 3 is appropriate — no additional syntax or format detail beyond what the schema carries.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Delete a Home Assistant automation') and adds 'permanently', which cleanly distinguishes it from the sibling mutators like ha_config_set_automation and ha_config_get_automation. An agent can identify the operation without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the destructive verb, and the examples show acceptable identifier forms, but there is no explicit when-to-use/when-not-to-use guidance or pointer to an alternative (e.g. disable vs delete). Nothing tells the agent when deletion is the wrong call versus ha_config_set_automation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_remove_calendar_eventRemove Calendar EventA
DestructiveIdempotent

Delete an event from a calendar.

Deletes a calendar event via the WebSocket calendar/event/delete command. HA's calendar component only registers create_event and get_events as REST services — delete and update live on the WebSocket API only. Get the event UID from ha_config_get_calendar_events().

EXAMPLES:

  • Delete a single event: ha_config_remove_calendar_event("calendar.family", uid="event-12345")

  • Delete one occurrence and all later ones: ha_config_remove_calendar_event("calendar.work", uid="recurring-event-67890", recurrence_id="20240115T100000", recurrence_range="THISANDFUTURE")

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
uidYesUnique identifier of the event to delete
entity_idYesCalendar entity ID (e.g., 'calendar.family')
recurrence_idNoRecurrence ID for recurring events
recurrence_rangeNoRecurrence range: 'THISANDFUTURE' to delete this and future occurrences. Home Assistant compares this value verbatim, so no other spelling (including 'THIS_AND_FUTURE') selects the range.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, idempotentHint=true and openWorldHint=false, so safety is covered. The description adds genuinely new context: deletion runs over the WebSocket API, the exact command path, and a concrete timeout/retry troubleshooting note for Claude Desktop. The repetitive 'read the target back before repeating' advice is a bit soft but does not mislead.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and the command, then examples. The trailing timeout paragraph is longer than the core spec and slightly dilutes focus, but each part still carries operational information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With a rich schema, destructive/idempotent annotations and an output schema, the description supplies everything else an agent needs: UID sourcing, recurring-event semantics, the transport quirk, and a failure-mode hint. Nothing material is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the examples and text go beyond the schema by showing how uid, recurrence_id and recurrence_range combine and by stressing the verbatim 'THISANDFUTURE' spelling. That is added semantic value over the property descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb+resource (delete a calendar event) with the exact WebSocket command named, and it explicitly distinguishes itself from the sibling set/get calendar event tools by noting delete lives only on the WebSocket API. An agent can identify the tool's role without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent to ha_config_get_calendar_events() for the UID, and the two worked examples show single-event vs recurring-range invocation. It stops short of stating when not to use it (e.g., use the set tool to modify instead), so it is clear context rather than full when/when-not coverage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_remove_categoryRemove CategoryA
DestructiveIdempotent

Delete a Home Assistant category.

Removes the category from the category registry for the given scope. This will also remove the category assignment from all entities in that scope.

EXAMPLES:

  • Delete category: ha_config_remove_category("automation", "my_category_id")

Use ha_config_get_category() to find category IDs.

WARNING: Deleting a category will remove it from all assigned entities. This action cannot be undone.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
scopeYesDomain scope for the category (e.g., 'automation', 'script', 'scene', 'helpers').
category_idYesID of the category to delete

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, but the description adds important context beyond annotations: deletion removes the category from the registry and from all assigned entities, and the action cannot be undone. It also includes an operational timeout note for Claude Desktop users.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The definition is front-loaded with the core action, scope, and warning, and the example is useful. The Claude Desktop timeout paragraph is lengthy and somewhat tangential to tool semantics, slightly reducing conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained. The description covers prerequisites, side effects, irreversibility, and an invocation example, making it complete enough for an agent to call the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both parameters are already documented in the schema. The description adds an example showing argument order and sample values, but does not add much parameter meaning beyond the schema baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (Delete/Removes) and resource (Home Assistant category), and clarifies the registry scope. It is clearly distinguishable from sibling get/set category tools by naming the delete action and its consequence.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It tells the agent to use ha_config_get_category() to find category IDs, which is a clear prerequisite. It also provides an example call and a strong warning that deletion cannot be undone, though it does not explicitly state when not to use this versus set/get alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_remove_groupRemove GroupA
DestructiveIdempotent

Remove a service-based Home Assistant entity group via the group.remove service.

When NOT to use: for groups created through ha_config_set_helper(helper_type="group", ...), use ha_remove_helpers_integrations. Those config-entry-backed groups are not reachable via the group.remove service.

When to use: removing groups created with ha_config_set_group or defined in YAML via group: configuration. Config-entry-backed deletion tools cannot find these.

EXAMPLES:

  • Remove group: ha_config_remove_group("living_room_lights")

Use ha_config_list_groups() to find existing groups.

WARNING:

  • Removing a group used in automations may cause those automations to fail.

  • Groups defined in YAML can be removed at runtime but will reappear after restart.

  • This only removes old-style groups, not platform-specific groups.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
waitNoWait for group to be fully removed before returning.
object_idYesGroup identifier without 'group.' prefix (e.g., 'living_room_lights')

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, but the description adds substantial context beyond them: automations referencing the group may fail, YAML-defined groups reappear after restart, and only old-style groups are affected. It also discloses operational behavior (4-minute timeout handling) that no structured field carries.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded and the when-not/when-to structure is easy to scan, but the closing Claude Desktop timeout paragraph is lengthy and only tangentially about invoking the tool correctly. It earns partial credit as actionable guidance but dilutes an otherwise tight definition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive removal tool with a full output schema, the description covers routing, prerequisites, side effects, and persistence caveats thoroughly. Nothing an agent needs to call it safely is missing, and return values are correctly left to the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both object_id (with prefix-exclusion and format) and the wait flag are already documented in the schema. The description only restates the identifier format via an example and never mentions the wait parameter, adding no meaning beyond the schema. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Remove a service-based Home Assistant entity group via the group.remove service') and immediately distinguishes it from the config-entry-backed path handled by ha_remove_helpers_integrations. An agent can tell this apart from ha_config_set_group and ha_config_list_groups without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-NOT-to-use (config-entry groups created via ha_config_set_helper → use ha_remove_helpers_integrations) and when-to-use (groups from ha_config_set_group or YAML `group:` config), plus a discovery pointer to ha_config_list_groups() and a concrete example. Both branches of the alternative-selection decision are covered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_remove_labelRemove LabelA
DestructiveIdempotent

Delete a Home Assistant label.

Removes the label from the label registry. This will also remove the label from all entities, devices, and areas that have it assigned.

EXAMPLES:

  • Delete label: ha_config_remove_label("my_label_id")

Use ha_config_get_label() to find label IDs.

WARNING: Deleting a label will remove it from all assigned entities. This action cannot be undone.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
label_idYesID of the label to delete

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint and idempotentHint, and the description goes well beyond them: it discloses cascade effects (removal propagates to all entities, devices, and areas), irreversibility, and operational timeout guidance. That is substantial behavioral context not available from structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded and the warning is well placed, but the final paragraph about a 4-minute Claude Desktop timeout and the manual-approve button is tangential troubleshooting chatter that does not help an agent select or invoke the tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, annotations cover the safety profile, and the description adds the cascade-destruction warning plus the ID-retrieval prerequisite. Everything an agent needs to call this correctly is present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter with 100% schema coverage, so the schema already carries the semantics and baseline is 3. The worked example ha_config_remove_label("my_label_id") adds the concrete string-ID usage form, giving a modest bump above baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (delete/remove) and resource (Home Assistant label) and clarifies it operates on the label registry, which cleanly separates it from siblings like ha_config_remove_category, ha_config_remove_scene, or ha_config_set_label.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear context: use ha_config_get_label() to find label IDs before calling. It does not name a when-not condition or an alternative deletion path, but the retrieval prerequisite is explicit, which is most of what an agent needs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_remove_sceneRemove SceneA
DestructiveIdempotent

Delete a Home Assistant scene.

EXAMPLE: ha_config_remove_scene("old_scene")

Only scenes created via the Home Assistant UI can be deleted. Scenes defined in YAML configuration files (scenes.yaml or configuration.yaml) cannot be deleted through the API and return a 405 Method Not Allowed error; edit the configuration file directly instead.

WARNING: Deleting a scene that is referenced by automations or scripts (via scene.turn_on) may cause those to fail.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
waitNoWait for scene to be fully removed before returning.
scene_idYesScene identifier to delete (e.g., 'old_scene')

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint and idempotentHint, but the description adds genuinely new behavioral detail: the 405 failure mode for file-defined scenes, the risk of breaking automations/scripts that call scene.turn_on, and the 4-minute-timeout/approval quirk. That is far beyond what the structured fields convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and example are front-loaded, then caveats, then the timeout note — a sensible order. The Claude Desktop timeout/approval paragraph is operationally useful but slightly tangential to the tool's semantics, keeping it short of a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need no explanation, and the description still covers the mutation's failure modes and blast radius. Nothing an agent needs to invoke this destructive tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and both parameters (scene_id, wait) are already documented in the schema. The description's example only echoes the scene_id format the schema already states, adding no syntax or default behavior beyond it, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Delete a Home Assistant scene') and immediately separates itself from ha_config_set_scene / ha_config_get_scene by its destructive action. The inline example pins down the calling form unambiguously.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit when-not condition (YAML-defined scenes cannot be deleted and return 405) plus a concrete alternative path (edit scenes.yaml/configuration.yaml directly), and warns about the consequence of deleting scenes referenced by automations. This is exactly the when/when-not/alternatives guidance the rubric reserves a 5 for.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_remove_scriptRemove ScriptA
DestructiveIdempotent

Delete a Home Assistant script.

EXAMPLE: ha_config_remove_script("old_script")

Only scripts created via the Home Assistant UI can be deleted. Scripts defined in YAML configuration files (scripts.yaml or configuration.yaml) cannot be deleted through the API and return a 405 Method Not Allowed error; edit the configuration file directly instead.

WARNING: Deleting a script that is used by automations may cause those automations to fail.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
waitNoWait for script to be fully removed before returning.
script_idYesScript identifier to delete — bare storage key ('old_script') or entity_id form ('script.old_script'); a leading 'script.' prefix is stripped before lookup.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, but the description goes further: it discloses the UI-vs-YAML restriction, the specific 405 error, the downstream automation-failure risk, and a timeout/re-approval workaround. That is substantial behavioral context beyond structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and the key restriction are front-loaded in the first two paragraphs, and the warning is clearly flagged. The final Claude Desktop timeout paragraph is somewhat long and environment-specific, but it is operationally useful rather than filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists and annotations carry the safety profile, so the description does not need to explain return values. For a destructive delete tool it covers everything an agent needs: what is deleted, what cannot be deleted, the error to expect, and the side-effect risk.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both script_id (including the 'script.' prefix-stripping behavior) and wait are already fully documented in the schema. The description only demonstrates script_id via an example and adds no new format or syntax detail, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Delete a Home Assistant script') and immediately scopes it with an example call. An agent can distinguish it from ha_config_set_script and ha_config_remove_automation without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states the when-not condition: only UI-created scripts can be deleted, while YAML-defined scripts return 405 and must be edited in the config file. It also warns that deletions may break dependent automations, giving the agent enough to advise a user before acting.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_automationCreate or Update AutomationA
Destructive

Create or update a Home Assistant automation.

MUST call ha_get_skill_guide OR refer to your locally installed skills first.

Prefer native triggers/conditions/actions over templates in logic positions; templates belong only in data.*, notification text, event_data and variables. The best-practice checker reports violations under best_practice_warnings — fix them before re-submitting. automation-patterns.md and template-guidelines.md ship under skill_content by default. Test any unavoidable template with ha_eval_template first.

Consider a dedicated tool first: a state snapshot with no trigger -> ha_config_set_scene; a value derived from other entities -> ha_config_set_helper(helper_type='template'); a counter / timer / schedule / boolean -> ha_config_set_helper.

MODES (pick one; each can also take enabled, which alone with identifier turns the automation on or off without touching config):

  • python_transform: surgical edits to an existing automation. Requires identifier and config_hash from ha_config_get_automation(). Operates on the fetched config, which uses HA's plural root keys 'triggers'/'actions'/'conditions', e.g. python_transform="config['triggers'].append({'trigger': 'state', 'entity_id': 'binary_sensor.motion', 'to': 'on'})"

  • config: new automations or full restructures. Regular automations need alias, triggers and actions; blueprint automations need alias and use_blueprint {path, input}.

  • take_control_of_blueprint: convert a blueprint-backed automation into a standalone one. Takes no config of its own.

IDENTITY: omit identifier and config['id'] to create with a generated ID; an unused raw ID creates that specific ID. Reusing an identifier targets the same automation even if the alias changes; to rename or replace it, read it first and pass its config_hash — a changed alias without that hash is rejected before writing. The returned automation_id is the resolved entity_id, falling back to the input identifier or the generated unique_id.

EXAMPLES:

  • Create: ha_config_set_automation(config={"alias": "Morning Lights", "triggers": [{"trigger": "time", "at": "07:00:00"}], "actions": [{"action": "light.turn_on", "target": {"area_id": "bedroom"}}]})

  • From a blueprint: ha_config_set_automation(config={"alias": "Motion Light Kitchen", "use_blueprint": {"path": "homeassistant/motion_light.yaml", "input": {"motion_entity": "binary_sensor.kitchen_motion", "light_target": {"entity_id": "light.kitchen"}}}})

  • Update: current = ha_config_get_automation(identifier="automation.x"); ha_config_set_automation(identifier="automation.x", config_hash=current["config_hash"], config={...})

  • Disable: ha_config_set_automation(identifier="automation.x", enabled=False)

  • Run now: ha_config_set_automation(identifier="automation.x", run_actions=True)

  • Take control: ha_config_set_automation(identifier="automation.x", take_control_of_blueprint=True)

TAKE CONTROL is one-way: later blueprint edits stop reaching the automation; to change an input value, update 'use_blueprint.input' instead. It does NOT free the blueprint — Home Assistant keeps counting the converted automation as a user, so deleting that blueprint stays refused until the automation is removed (an automation reload does not clear it). The response names the blueprint in took_control_of_blueprint. To preview the rendering without writing anything, use ha_manage_blueprints(action="substitute", path=..., input=...).

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

create update modify edit automation triggers conditions actions new automation write save take control blueprint detach unlink standalone convert enable disable turn on off run trigger now

ParametersJSON Schema
NameRequiredDescriptionDefault
waitNoWait for automation to be queryable before returning. Set to False for bulk operations.
configNoComplete automation configuration. Purpose-specific triggers/conditions (HA 2026.7+ default: 'trigger': '<domain>.<name>' with 'target'/'options') are valid config. Mutually exclusive with python_transform.
enabledNoTurn the automation on (True) or off (False) after an optional config update; None leaves it unchanged. Not written into the stored config: Home Assistant keeps the state across restarts, but a config 'initial_state' overrides it whenever the automation is reloaded or HA starts.
categoryNoCategory ID to assign to this automation. Use ha_config_get_category(scope='automation') to list available categories, or ha_config_set_category() to create one.
identifierNoTarget automation entity_id or HA config 'id' (unique_id). Omit for creation with a generated ID. Values such as 'new' are literal IDs, not placeholders.
config_hashNoConfig hash from ha_config_get_automation for optimistic locking. Required for python_transform and when a config update changes an existing automation's alias; otherwise optional for config updates (validates before full replacement if provided).
run_actionsNoRun the automation's actions now, skipping its triggers and conditions (automation.trigger, the UI's Run actions). Applied after `enabled` and after any config write. Can be used standalone with identifier and no config.
MandatoryBPSNo
BestPracticeKeyNoRead-receipt for the home-assistant-best-practices skill; required when strict best-practices mode is enabled. Not a secret or credential: the current value is an attestation phrase published openly at the top of the skill content served by ha_get_skill_guide. Read that content, then pass the value back verbatim — this round-trip is the server's designed protocol confirming the practices were read before writing.
python_transformNoPython expression to transform existing automation config. Mutually exclusive with config. WARNING: Expressions with infinite loops will hang the server. Examples: Simple: python_transform="config['actions'][0]['data']['brightness'] = 255" Pattern: python_transform="for a in config['actions']: if a.get('alias') == 'My Step': a['data']['value'] = 100" PYTHON TRANSFORM SECURITY: ✅ ALLOWED: - Dictionary/list access: config['views'][0]['cards'][1] - Slicing: config['views'][0]['cards'][1:3] - Assignment: config['key'] = 'value' - Deletion: del config['key'] or config.pop('key') - List methods: append, insert, pop, remove, clear, extend - Dict methods: update, get, setdefault, keys, values, items - Loops: for, if/else, pass, break, continue - Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...) - Ternary: x if condition else y - Iterable unpacking (* in calls/literals): f(*xs), [*xs, y] - Dict unpacking (**) in calls and dict literals: {**d, 'k': v} - Keyword arguments: func(key=value) - Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score']) - String methods: startswith, endswith, lower, upper, strip, split, join, replace - Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed, min, max, sum, abs, any, all, round, str, int, float, bool, list, dict, tuple, set ❌ FORBIDDEN: - Imports: import, from, __import__ - File operations: open, read, write - Dunder access: __class__, __bases__, __subclasses__ - Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr - Function definitions: def, class - Exception handling: try/except (validate with isinstance/in/.get() instead) - While loops: use bounded for loops or comprehensions instead
take_control_of_blueprintNoConvert a blueprint-backed automation into an editable standalone one -- the UI's "Take control". Renders the blueprint with its current inputs and saves the result over the same automation, which keeps its entity_id, alias and description and then has its own triggers/conditions/actions and no 'use_blueprint'. Requires identifier; mutually exclusive with config and python_transform.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint=true, but the description adds substantial non-structured context: take-control is one-way and does not free the blueprint (HA keeps counting the automation as a user), a changed alias without config_hash is rejected before writing, and the Claude Desktop 4-minute timeout recovery procedure. This is the behavioral depth annotations cannot carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Long, but sectioned (MUST, MODES, IDENTITY, EXAMPLES, TAKE CONTROL) with the hard requirements front-loaded before the detail. Each block earns its place as critical routing or safety information, though the six examples partially restate the schema-documented parameters.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 11-parameter mutation tool with an output schema and annotations present, the description covers creation, update, rename, disable, run-now, take-control, blueprint conversion, and the BestPracticeKey protocol. Nothing an agent needs to call this correctly appears to be missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already high (91%), so much parameter meaning is in the schema. The description still adds value beyond it: the identity/rename rejection rule, the relationship between identifier and config['id'], and the per-mode usage of config vs python_transform vs take_control_of_blueprint via concrete examples. A few semantics (e.g. category, wait) are left to the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb pair (create or update) on a specific resource (Home Assistant automation), and immediately differentiates from siblings by routing non-automation logic to ha_config_set_scene and ha_config_set_helper with explicit conditions. An agent can tell this apart from the other config_set_* tools without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit preconditions (MUST call ha_get_skill_guide first), a prefer-native-over-templates rule, a routing table for the dedicated scene/helper alternatives, three mutually exclusive modes with per-mode requirements, and six worked examples. When-to-use and when-to-avoid are both covered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_calendar_eventCreate or Update Calendar EventA
Destructive

Create a new event in a calendar, or update an existing one.

Creates a one-off event via the calendar.create_event service, or a recurring series via the WebSocket calendar/event/create command when rrule is provided (the REST service schema does not accept recurrence rules). Passing uid switches to update mode, which uses the WebSocket calendar/event/update command — HA registers no REST service for updating an event.

When NOT to use:

  • To retrieve calendar events, use ha_config_get_calendar_events.

  • To delete an event, use ha_config_remove_calendar_event.

  • To find the uid of an event to update, use ha_config_get_calendar_events; this tool does not search.

Passing date-only values (YYYY-MM-DD) for both start and end creates an all-day event; passing full ISO datetimes creates a timed event. The two forms cannot be mixed — a date-only start with a datetime end (or vice versa) is rejected.

An update replaces the whole event rather than patching it, so summary, start and end stay required in update mode, and a description or location that is not re-supplied is cleared. An rrule is the exception: Home Assistant accepts a new rule but has no way to express "no recurrence", so an existing rule survives an update that omits it. Delete the event and create it again to drop the recurrence.

Not every calendar integration supports event creation; recurring events additionally require the integration to support recurrence (the built-in Local Calendar does). Update support is narrower still: Local Calendar implements it, while the core Google Calendar and CalDAV integrations do not.

EXAMPLES:

  • Create: ha_config_set_calendar_event("calendar.family", summary="Doctor appointment", start="2024-01-15T14:00:00", end="2024-01-15T15:00:00")

  • Recurring (every Monday, 10 occurrences): ha_config_set_calendar_event("calendar.work", summary="Team meeting", start="2024-01-15T10:00:00", end="2024-01-15T11:00:00", rrule="FREQ=WEEKLY;BYDAY=MO;COUNT=10")

  • Update this and all later occurrences: ha_config_set_calendar_event("calendar.work", summary="Team meeting (new time)", start="2024-02-05T11:00:00", end="2024-02-05T12:00:00", uid="recurring-event-67890", recurrence_id="20240205T100000", recurrence_range="THISANDFUTURE")

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
endYesEvent end date or datetime in ISO format. For all-day events (date-only) the end date is exclusive; a single-day all-day event needs end = start + 1 day.
uidNoUID of an existing event to update. Omit to create a new event.
rruleNoRFC 5545 recurrence rule, without 'RRULE:' prefix (e.g., 'FREQ=WEEKLY;BYDAY=MO' or 'FREQ=MONTHLY;BYDAY=3SA'). Creates a recurring event series.
startYesEvent start date or datetime in ISO format
summaryYesEvent title/summary
locationNoEvent location
entity_idYesCalendar entity ID (e.g., 'calendar.family')
descriptionNoEvent description
recurrence_idNoOnly meaningful with 'uid': identifies one occurrence of a recurring series to update.
recurrence_rangeNoOnly meaningful with 'uid': 'THISANDFUTURE' to update this and all following occurrences. Home Assistant compares this value verbatim, so no other spelling (including 'THIS_AND_FUTURE') selects the range.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only flag destructiveHint and openWorldHint; the description adds substantial context beyond them: update replaces the whole event and clears un-supplied description/location, rrule survives an update (no way to express 'no recurrence'), date-only vs datetime forms cannot be mixed, and a Claude Desktop timeout/approval workaround. Nothing here contradicts the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core create/update/mode-switch behavior, then uses a bolded exclusions block and concrete examples. It is long for a tool description and the trailing Claude Desktop timeout/approval paragraph is somewhat digressive, but the structure keeps it navigable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need no explanation. Given 10 parameters, three modes, and integration-dependent limitations, the description covers the decision-relevant behavior (mode selection, field replacement, recurrence edge cases) without gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds real meaning: uid is the mode switch into WebSocket update, rrule requires the WebSocket path because the REST service schema rejects recurrence, and recurrence_id/recurrence_range only make sense with uid. Some overlap with schema text (e.g., the end-date exclusivity) keeps it just below the top.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb+resource and immediately disambiguates the dual mode: create a one-off event, create a recurrence via WebSocket, or switch to update mode by passing uid. An agent can distinguish this from the get/remove calendar siblings without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Contains an explicit 'When NOT to use' block routing to ha_config_get_calendar_events and ha_config_remove_calendar_event, plus the condition for finding a uid. It further narrows applicability with an integration-support matrix (Local Calendar supports create/update; Google Calendar and CalDAV do not).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_categoryCreate or Update CategoryA
Destructive

Create or update a Home Assistant category.

Creates a new category if category_id is not provided, or updates an existing category if category_id is provided.

Categories are domain-scoped organizational groups for automations, scripts, scenes, and helpers. Unlike labels (which are cross-domain), categories are specific to a single domain scope.

EXAMPLES:

  • Create automation category: ha_config_set_category("Lighting", scope="automation")

  • Create with icon: ha_config_set_category("Security", scope="automation", icon="mdi:shield")

  • Update category: ha_config_set_category("Updated Name", scope="automation", category_id="my_category_id")

After creating a category, use ha_set_entity(categories={"automation": "category_id"}) to assign it.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
iconNoMaterial Design Icon (e.g., 'mdi:tag', 'mdi:label')
nameYesDisplay name for the category
scopeYesDomain scope for the category (e.g., 'automation', 'script', 'scene', 'helpers').
category_idNoCategory ID for updates.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations supply openWorldHint=false and destructiveHint=true, so the description doesn't need to restate scope/safety. The description adds genuine behavioral context: the create-vs-update branching driven by category_id, the domain-scoping model, follow-up assignment flow, and a Claude Desktop timeout/approval caveat. It does not cover the destructiveHint implication (what an update overwrites) or permission needs, keeping it short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Content is front-loaded with the core create/update rule, followed by a concise conceptual note, examples, and follow-up guidance. The final timeout paragraph is verbose relative to the rest but is genuinely actionable, so it earns most of its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be described. The description covers creation semantics, scoping, follow-up assignment, and an operational caveat, leaving only the destructive-update semantics unaddressed. Nearly complete for a 4-parameter config tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning beyond the schema by explaining that category_id's presence toggles create vs update behavior and by demonstrating icon and scope syntax in examples. This is more than the schema fields alone convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb+resource ('create or update a Home Assistant category') and explicitly distinguishes categories from labels ('cross-domain') and clarifies categories are domain-scoped. An agent can tell this apart from ha_config_set_label or ha_config_set_automation without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly explains the conditional branch: create when category_id is absent, update when present, and gives concrete examples for both. It also tells the agent to follow up with ha_set_entity to assign the category. It stops short of naming explicit alternatives (e.g., ha_config_get_category / ha_config_remove_category) or when-not-to-use conditions, so it is clear but not exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_dashboardCreate or Update DashboardA
Destructive

Create or update a Home Assistant dashboard.

MUST call ha_get_skill_guide OR refer to your locally installed skills first. dashboard-guide.md and dashboard-cards.md ship under skill_content by default.

MODES (pick one):

  • patch: edit known paths with literal values using JSON Patch add/remove/replace/test and config_hash, e.g. patch=[{"op": "replace", "path": "/views/0/title", "value": "Home"}]. move/copy are unsupported. Full guide: https://github.com/homeassistant-ai/ha-mcp/blob/master/docs/dashboard-edits.md

  • python_transform: loops or pattern-based changes across cards and views, e.g. 'config["views"][0]["cards"].append({"type": "button", "entity": "light.bedroom"})'. After delete/add operations indices shift, so chain multiple ops in ONE expression where possible; a later call needs a fresh config_hash from ha_config_get_dashboard().

  • config: new dashboards only, or a full restructure. Replaces everything. Omit it to create a dashboard without initial config.

Use ha_config_get_dashboard(entity_id=...) to get the path of any card, and ha_search / ha_get_overview to find entity IDs — never guess them. For visual re-checks after the write, use ha_get_dashboard_screenshot (when available) instead of re-sending config.

title/icon/require_admin/show_in_sidebar can be updated in a metadata-only call or alongside a full config replacement; with python_transform or patch, update metadata in a separate call (combining it with patch is rejected). Strategy dashboards (config {"strategy": {...}}) cannot be converted to custom dashboards here; use "Take Control" in the Home Assistant UI.

EXAMPLE: ha_config_set_dashboard(url_path="home-dashboard", title="Home Overview", config={"views": [{"title": "Home", "type": "sections", "sections": [{"title": "Climate", "cards": [{"type": "tile", "entity": "climate.living_room"}]}]}]})

This tool manages storage-mode dashboards only. YAML-mode dashboards (a .yaml file registered under lovelace: dashboards: in configuration.yaml) and dashboards inlined under lovelace: are edited in their .yaml file; ha_config_set_yaml can update the lovelace: registration but not the dashboard body.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
iconNoMDI icon name (e.g., 'mdi:home', 'mdi:cellphone'). Defaults to 'mdi:view-dashboard'
patchNoStructured dashboard edits: up to 100 JSON Patch add, remove, replace or test operations using RFC 6901 paths. Use /- to append to an array; escape ~ as ~0 and / as ~1 in keys. Mutually exclusive with config and python_transform. Strings in value are preserved literally.
titleNoDashboard display name shown in sidebar
configNoDashboard configuration with views and cards. Omit or set to None to create dashboard without initial config. Mutually exclusive with python_transform and patch.
url_pathYesDashboard URL path (e.g., 'my-dashboard'). Use 'default' or 'lovelace' for the default dashboard. New dashboards must use a hyphenated path.
view_pathNoWith return_screenshot: stable Lovelace views[].path to render.
config_hashNoConfig hash from ha_config_get_dashboard for optimistic locking. REQUIRED for python_transform and patch (validates dashboard unchanged). Optional for config (validates before full replacement if provided).
MandatoryBPSNo
require_adminNoRestrict dashboard to admin users only. For existing dashboards, only updated when explicitly provided.
BestPracticeKeyNoRead-receipt for the home-assistant-best-practices skill; required when strict best-practices mode is enabled. Not a secret or credential: the current value is an attestation phrase published openly at the top of the skill content served by ha_get_skill_guide. Read that content, then pass the value back verbatim — this round-trip is the server's designed protocol confirming the practices were read before writing.
show_in_sidebarNoShow dashboard in sidebar navigation. For existing dashboards, only updated when explicitly provided.
python_transformNoPython expression to transform existing dashboard config. Mutually exclusive with config and patch. Requires config_hash for validation. See PYTHON TRANSFORM SECURITY below for allowed operations. Examples: Simple: python_transform="config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'" Pattern: python_transform="for card in config['views'][0]['cards']: if 'light' in card.get('entity', ''): card['icon'] = 'mdi:lightbulb'" Multi-op: python_transform="config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'; del config['views'][0]['cards'][2]" PYTHON TRANSFORM SECURITY: ✅ ALLOWED: - Dictionary/list access: config['views'][0]['cards'][1] - Slicing: config['views'][0]['cards'][1:3] - Assignment: config['key'] = 'value' - Deletion: del config['key'] or config.pop('key') - List methods: append, insert, pop, remove, clear, extend - Dict methods: update, get, setdefault, keys, values, items - Loops: for, if/else, pass, break, continue - Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...) - Ternary: x if condition else y - Iterable unpacking (* in calls/literals): f(*xs), [*xs, y] - Dict unpacking (**) in calls and dict literals: {**d, 'k': v} - Keyword arguments: func(key=value) - Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score']) - String methods: startswith, endswith, lower, upper, strip, split, join, replace - Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed, min, max, sum, abs, any, all, round, str, int, float, bool, list, dict, tuple, set ❌ FORBIDDEN: - Imports: import, from, __import__ - File operations: open, read, write - Dunder access: __class__, __bases__, __subclasses__ - Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr - Function definitions: def, class - Exception handling: try/except (validate with isinstance/in/.get() instead) - While loops: use bounded for loops or comprehensions instead
return_screenshotNoAfter writing, also return rendered image(s) of the dashboard. Requires the 'dashboard screenshot' beta feature + engine app (add-on)/sidecar; if unavailable, the write result is returned with a warning.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations give destructiveHint=true, and the description goes well beyond that: it warns config 'replaces everything', that combining metadata edits with patch is rejected, that config_hash is required for patch/python_transform, that YAML-mode and strategy dashboards can't be handled here, and documents a real timeout/retry failure mode. This is exactly the behavioral context annotations can't carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then structured MODES, a worked example, and caveats. Almost every section earns its place given 13 parameters and three mutually exclusive paths, though the Claude Desktop timeout paragraph and some repeated mutual-exclusivity notes push it toward the verbose side.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, multi-mode, 13-parameter tool with no output schema, the description is complete: it covers mode selection, locking via config_hash, storage-vs-YAML boundaries, metadata-update rules, entity-lookup guidance, and failure recovery. An agent has everything needed to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 92%, so the baseline is 3. The description adds genuine mode semantics for the three mutually exclusive write parameters (patch ops, python_transform chaining, config replacement) and explains why config_hash must be refreshed between calls, which the schema does not spell out.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a precise verb+resource ('Create or update a Home Assistant dashboard') and immediately scopes it further (storage-mode dashboards only). It also names the sibling tools it coordinates with (ha_config_get_dashboard, ha_config_delete_dashboard, ha_get_dashboard_screenshot), so an agent can place it in the CRUD set without guessing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly enumerates three mutually exclusive modes with the condition that selects each (patch for known paths, python_transform for loops/pattern changes, config for new/full restructure). It states when-not (YAML-mode dashboards are edited elsewhere), prerequisites (MUST call ha_get_skill_guide first), and routes visual re-checks to a different tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_dashboard_resourceSet Dashboard ResourceA
Destructive

Create or update a dashboard resource (inline code or external URL).

Provide exactly one of:

  • content: Inline JavaScript or CSS code (embedded in the resource URL as a data: URI — no file storage or external hosting involved). Supports 'module' and 'css' types only (not 'js'); URLs are deterministic (same content = same URL); up to ~128KB. Content must be self-contained: a data: URI has no base URL, so relative imports inside a module and relative url() references inside CSS cannot resolve (use fully-qualified URLs instead). If Home Assistant is behind a reverse proxy that injects a Content-Security-Policy without 'data:' in script-src/style-src, the browser blocks these resources: this call still succeeds and the card simply never renders. Register the code as a file and use url='/local/...' on such a deployment. (HA itself ships no CSP.)

  • url: External resource URL — /local/... (files in /config/www/), /hacsfiles/... (HACS-installed cards) or https://... (CDN). Supports all types: 'module', 'js', 'css'.

EXAMPLES:

  • Inline: ha_config_set_dashboard_resource(content="class MyCard extends HTMLElement { ... } customElements.define('my-card', MyCard);", resource_type="module")

  • HACS card (after ha_manage_hacs(action='download')): ha_config_set_dashboard_resource(url="/hacsfiles/lovelace-mushroom/mushroom.js", resource_type="module")

  • Update: ha_config_set_dashboard_resource(url="/local/my-card-v2.js", resource_type="module", resource_id="abc123")

After adding a resource, a browser cache clear or hard refresh (Ctrl+Shift+R) is needed to load it.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoURL of the resource.
contentNoJavaScript or CSS code to host inline.
resource_idNoResource ID to update. If omitted, creates a new resource. Get IDs from ha_config_list_dashboard_resources()
resource_typeNoResource type: 'module' for ES6 modules (modern cards), 'js' for legacy JavaScript (older custom cards), 'css' for stylesheets (themes, global styles)module

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only supply destructiveHint=true and openWorldHint=false; the description adds far more: deterministic data: URI generation, the ~128KB cap, self-containment requirements, the silent-failure mode where the resource is saved but never renders under CSP, the required hard refresh, and timeout/approval guidance. This is unusually rich disclosure for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the action and the mutually exclusive parameter rule, then uses bullets for detail. It runs long and the three examples plus the Claude Desktop timeout note are verbose, but nearly every sentence carries actionable information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-parameter mutation tool with an output schema (so return values need not be described), the description covers the failure modes, caching requirement, reverse-proxy caveat, and update-vs-create path (resource_id omitted = create), leaving no meaningful gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline would be 3, but the description adds constraints absent from the schema, notably that content supports only 'module' and 'css' (not 'js'), the ~128KB limit, and that relative imports/url() cannot resolve inside a data: URI. These materially change how an agent parameterizes the call.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource pair ('Create or update a dashboard resource') and immediately scopes the two mutually exclusive modes (inline content vs external URL). It is clearly distinguishable from siblings like ha_config_list_dashboard_resources and ha_config_delete_dashboard_resource.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states 'Provide exactly one of' for content vs url, describes which resource types each mode supports, names the alternative approach when a reverse proxy injects CSP ('Register the code as a file and use url=/local/...'), and gives concrete example flows including the HACS prerequisite order.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_groupCreate or Update GroupA
Destructive

Create or update a service-based Home Assistant entity group via the group.set service.

When NOT to use: for typical "combine these entities into one controllable group" requests, prefer ha_config_set_helper(helper_type="group", ...). Config-entry-backed groups are registered in the entity registry, so ha_set_entity can assign them to areas and they are deletable via ha_remove_helpers_integrations.

When to use: compatibility with existing groups already configured via group.set or YAML, or the rare case where entity-registry membership is explicitly unwanted. Groups created here are only removable via ha_config_remove_group — ha_remove_helpers_integrations will not find them.

For NEW groups: Provide object_id and entities (required). For EXISTING groups: Provide object_id and any fields to update. entities, add_entities, and remove_entities are mutually exclusive.

EXAMPLES:

  • Create: ha_config_set_group("bedroom_lights", entities=["light.lamp", "light.ceiling"], name="Bedroom Lights")

  • Add entities: ha_config_set_group("lights", add_entities=["light.extra"])

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
iconNoMaterial Design Icon (e.g., 'mdi:lightbulb-group')
nameNoFriendly display name for the group
waitNoWait for group to be queryable before returning. Set to False for bulk operations.
all_onNoIf True, all entities must be on for group to be on
entitiesNoList of entity IDs for the group. When updating, replaces all entities.
object_idYesGroup identifier without 'group.' prefix (e.g., 'living_room_lights')
add_entitiesNoAdd these entities to an existing group
remove_entitiesNoRemove these entities from an existing group

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With annotations already declaring destructiveHint=true and openWorldHint=false, the description adds substantial behavioral context: groups created here are only removable via ha_config_remove_group, ha_remove_helpers_integrations will not find them, and it notes a Claude Desktop timeout/approval workaround. These details go well beyond the annotations and help the agent avoid misleading cleanup paths.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured with clear sections for exclusions, usage, new/existing behavior, mutual exclusivity, and examples. It is front-loaded and mostly earns its space, though the Claude Desktop timeout paragraph is somewhat long and operationally tangential.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an 8-parameter tool with a rich schema, an output schema, and sibling alternatives, the description covers the critical gaps: when to use it versus ha_config_set_helper, how new and existing groups differ, mutual exclusivity, removal behavior, and timeout handling. Nothing essential for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds important semantic constraints not present in the schema: entities, add_entities, and remove_entities are mutually exclusive, new groups require object_id and entities, and existing groups should provide only fields to update. It still does not explain every parameter such as wait, icon, or all_on, so it earns a strong 4 rather than a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: create or update a service-based Home Assistant entity group via group.set. It immediately differentiates itself from the preferred sibling path, ha_config_set_helper(helper_type="group"), so an agent can distinguish the two without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit When NOT to use guidance naming ha_config_set_helper as the preferred alternative, and explicit When to use guidance for compatibility with existing group.set/YAML groups or when entity-registry membership is unwanted. This is exactly the routing information an agent needs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_helperCreate or Update HelperA
Destructive

Create or update Home Assistant helper entities and config subentries (30 types, unified interface).

MUST call ha_get_skill_guide OR refer to your locally installed skills first. helper-selection.md ships under skill_content by default.

SIMPLE types (structured params, WebSocket API): input_boolean, input_button, input_select, input_number, input_text, input_datetime, counter, timer, schedule, zone, person, tag. Create requires name; update requires helper_id.

FLOW types (pass config dict, Config Entry Flow API): template, group, utility_meter, derivative, min_max, threshold, integration, statistics, trend, random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat, history_stats, mold_indicator. Create requires name; for updates pass the existing entry_id as helper_id (options flows reject the name key). otp is a helper in the HA UI but not offered here — its flow needs a live TOTP code; create it with ha_set_integration(domain="otp").

CONFIG_SUBENTRY type (Config Subentry Flow API): pass entry_id, subentry_type and config; pass subentry_id to reconfigure an existing subentry, omit it to create one.

Behavior:

  • UPDATE preserves type-specific fields not re-passed (a rename never wipes initial/icon/etc.); flow-helper and config subentry updates behave the same way (see config).

  • Omitted action falls back to the helper_id-presence discriminator (SIMPLE/FLOW) or the subentry_id-presence discriminator (config subentries).

  • For flow-based helpers, config keys not declared by any step's data_schema are silently ignored by HA. Validation errors carry the helper's data_schema (and menu_options for menu-rooted helpers like template/group when no sub-type is chosen yet), so a follow-up call can self-correct without a separate schema-discovery round-trip.

  • Flows that present more than one menu (e.g. an MQTT device subentry reconfigure looping through its summary menu) take next_step_id as a LIST of successive selections, consumed one per menu encounter.

EXAMPLES (menu-based types, where the first-call payload is non-obvious):

  • template sensor: ha_config_set_helper(helper_type="template", name="Room Temp", config={"next_step_id": "sensor", "state": "{{ states('sensor.x')|float }}", "unit_of_measurement": "°C"})

  • group: ha_config_set_helper(helper_type="group", name="Kitchen Lights", config={"group_type": "light", "entities": ["light.a", "light.b"]})

  • config subentry: ha_config_set_helper(helper_type="config_subentry", entry_id="01HXYZ...", subentry_type="conversation", config={"name": "Local agent", "model": "gemma3:27b"})

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

create update new add helper input_boolean input_button input_number input_text input_datetime input_select counter timer schedule zone person tag template group utility_meter derivative min_max threshold integration statistics trend random filter tod generic_thermostat switch_as_x generic_hygrostat history_stats mold_indicator

ParametersJSON Schema
NameRequiredDescriptionDefault
iconNoMaterial Design Icon (e.g., 'mdi:bell', 'mdi:toggle-switch')
modeNoDisplay mode: 'box'/'slider' for input_number, 'text'/'password' for input_text
nameNoDisplay name for simple/flow helper creation. Optional on helper update. Ignored for helper_type='config_subentry', which uses entry_id/subentry_type/subentry_id instead. For flow-based helper updates (template, group, utility_meter, ...), this is ignored because options flows don't expose renaming; change the resulting entity's display name with ha_set_entity(name=...).
stepNoStep/increment value for input_number or counter
waitNoWait for helper entity to be queryable before returning. Set to False for bulk operations.
actionNoExplicit intent: 'create' a new helper or 'update' an existing one. Pass it so a helper_id typo fails as 'helper not found' instead of creating a helper.
configNoConfig dict for flow-based helper types and helper_type='config_subentry'. Ignored for simple helper types. On update it is a patch: a field you omit keeps its current value, and a field set to null is cleared where the schema allows that field to be empty. A field two steps declare gets your one value both times; pass step_values={'<step_id>': {'<field>': <value>}} to give a step its own value, or to leave it out of that step; a LIST of those objects supplies one per encounter when the flow presents a step more than once.
fridayNoSchedule time ranges for Friday; same shape as monday.
labelsNoLabels to categorize the helper
mondayNoSchedule time ranges for Monday. List of {'from': 'HH:MM', 'to': 'HH:MM'} dicts. Optional 'data' dict for additional attributes (e.g. {'from': '07:00', 'to': '22:00', 'data': {'mode': 'comfort'}})
radiusNoRadius in meters for zone (default: 100)
sundayNoSchedule time ranges for Sunday; same shape as monday.
tag_idNoTag ID. On create, omit to auto-generate a uuid4 hex. On update, the existing tag_id is required (passed via helper_id).
area_idNoArea/room ID to assign the helper to
initialNoInitial value for applicable helper types. For input_boolean, input_select, input_number, input_text, and input_datetime: setting `initial` — even to false/0 — disables last-state restore and forces that value on every HA restart; omit unless you want the helper to reset to that value on every restart instead of restoring its last state. For counter, `initial` is just the starting value — restore-on-restart is controlled separately by `restore` (default True).
optionsNoList of options for input_select (required for input_select)
passiveNoPassive zone (won't trigger state changes for person entities)
pictureNoPicture URL for person entity
restoreNoRestore state after restart (counter, timer). Defaults to True for counter, False for timer
tuesdayNoSchedule time ranges for Tuesday; same shape as monday.
user_idNoUser ID to link to person entity
categoryNoCategory ID to assign to this helper. Use ha_config_get_category(scope='helpers') to list available categories, or ha_config_set_category() to create one.
durationNoDefault duration for timer in format 'HH:MM:SS' or seconds (e.g., '0:05:00' for 5 minutes)
entry_idNoParent config entry ID when helper_type='config_subentry'. Use ha_get_integration() to find entry IDs.
has_dateNoInclude date component for input_datetime
has_timeNoInclude time component for input_datetime
latitudeNoLatitude for zone (required for zone)
saturdayNoSchedule time ranges for Saturday; same shape as monday.
thursdayNoSchedule time ranges for Thursday; same shape as monday.
helper_idNoBare ID ('my_button') or full entity ID ('input_button.my_button'). Omit to create a new helper.
longitudeNoLongitude for zone (required for zone)
max_valueNoMaximum value (input_number/counter) or maximum length (input_text). Also accepts shorthand 'max'.
min_valueNoMinimum value (input_number/counter) or minimum length (input_text). Also accepts shorthand 'min'.
wednesdayNoSchedule time ranges for Wednesday; same shape as monday.
descriptionNoDescription for tag
helper_typeYesType of helper entity to create or update
subentry_idNoExisting config subentry ID to reconfigure when helper_type='config_subentry'.
MandatoryBPSNo
subentry_typeNoIntegration-defined subentry type when helper_type='config_subentry'.
BestPracticeKeyNoRead-receipt for the home-assistant-best-practices skill; required when strict best-practices mode is enabled. Not a secret or credential: the current value is an attestation phrase published openly at the top of the skill content served by ha_get_skill_guide. Read that content, then pass the value back verbatim — this round-trip is the server's designed protocol confirming the practices were read before writing.
device_trackersNoList of device_tracker entity IDs for person
unit_of_measurementNoUnit of measurement for input_number (e.g., '°C', '%', 'W'). Also accepts shorthand 'unit'.
show_advanced_optionsNoWhen helper_type='config_subentry', ask older Home Assistant versions to expose advanced flow options. No-op on HA 2026.6+; pending removal before HA 2027.6.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the destructiveHint/openWorldHint annotations: update is a patch (omitted fields preserved, null clears where allowed), action falls back to a helper_id/subentry_id presence discriminator, flow config keys outside the step data_schema are silently ignored, validation errors return data_schema/menu_options for self-correction, next_step_id is consumed as a list for multi-menu flows, and it even documents a client-side timeout/misapproval recovery. This is unusually rich behavioral disclosure for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded and logically ordered (purpose → prerequisite → type families → behavior → examples), but it is very long and ends with a bare keyword dump of all 29 helper type names that simply restates the helper_type enum and adds no information. The Claude Desktop timeout paragraph, while practically useful, is operational trivia that dilutes the core contract.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-value description is correctly omitted. Given 43 parameters, three distinct API families, and destructive update semantics, the description supplies the create/update contract, discriminator rules, patch semantics, and worked examples for the non-obvious menu-based types. Nothing an agent needs to invoke this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 98%, so the schema already carries the field-level burden and baseline is 3. The description still adds genuine semantics the schema cannot: why to pass action ('a helper_id typo fails as helper not found instead of creating a helper'), the helper_id-presence discriminator for create vs update, step_values for fields two steps declare, and concrete config payloads for menu-rooted types. It stops short of full coverage only because it defers most per-field detail to the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Create or update Home Assistant helper entities and config subentries') with scope (30 types, unified interface). It explicitly partitions the domain into SIMPLE / FLOW / CONFIG_SUBENTRY families and routes out-of-scope work to siblings (otp via ha_set_integration, rename via ha_set_entity), so an agent can distinguish it from ha_config_remove_helpers_integrations and ha_set_entity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit prerequisites (MUST call ha_get_skill_guide or consult local skills, helper-selection.md under skill_content), selects the correct parameter family per helper type, and names alternatives and their trigger conditions for otp and renaming. When-to-use and when-to-go-elsewhere are both spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_labelCreate or Update LabelA
Destructive

Create or update a Home Assistant label.

Creates a new label if label_id is not provided, or updates an existing label if label_id is provided. Labels are a cross-domain tagging system for entities, devices, and areas.

EXAMPLES:

  • Create: ha_config_set_label("Security", color="red", icon="mdi:shield", description="Security-related devices")

  • Update: ha_config_set_label("Updated Name", label_id="my_label_id", color="blue")

  • Create and apply to areas: ha_config_set_label("Site Home", areas=["kitchen", "living_room"])

After creating a label, use ha_set_entity(labels=["label_id"]) to assign it to entities, ha_set_device(labels=["label_id"]) for devices, or ha_set_area_or_floor(kind="area", labels=["label_id"]) for areas (replaces the area's set).

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
iconNoMaterial Design Icon (e.g., 'mdi:tag', 'mdi:label')
nameYesDisplay name for the label
areasNoArea IDs to apply this label to (adds the label without removing existing ones). Omit to leave area assignments unchanged; an empty list is a no-op (assigns nothing and removes nothing). To clear an area's labels use ha_set_area_or_floor(kind='area', labels=[]).
colorNoColor for the label (e.g., 'red', 'blue', 'green', or hex like '#FF5733')
label_idNoLabel ID for updates.
descriptionNoDescription of the label's purpose

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and openWorldHint=false, so the mutation risk is known. The description adds real value beyond that: the areas parameter's additive-but-replacing semantics, the 4-minute Claude Desktop timeout with explicit remediation (read back before retrying, wait before approving), and sibling calls needed to complete labeling. It stops short of saying whether omitted fields on an update are preserved or cleared, which matters for a destructive-hint tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and the create/update rule are front-loaded in the first three lines, and the EXAMPLES section is scannable and earns its space. The final paragraph on Claude Desktop timeouts is long and operationally wordy relative to the rest, slightly diluting focus for a tool that is otherwise tight.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need no explanation, and the description covers both modes, cross-tool follow-up, and a real failure mode. The remaining gap is update semantics for non-area fields (whether omitting color/icon/description clears them), which an agent needs before performing a destructiveHint update.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so every parameter including the tricky areas merge/no-op semantics is already documented in the schema, making 3 the baseline. The EXAMPLES block shows realistic parameter combinations and functionally reinforces the schema's create-vs-update contract, but adds no format or constraint detail the schema lacks.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb pair (create/update) plus the resource (Home Assistant label) and immediately disambiguates the two modes by the presence of label_id. It also explains the concept domain ('cross-domain tagging system for entities, devices, and areas'), which distinguishes it from the sibling config_set_* tools that target single resource types.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use conditions (no label_id = create, label_id = update) backed by three concrete examples, and routes to the correct siblings for follow-up work: ha_set_entity, ha_set_device, ha_set_area_or_floor. It also notes the alternative for clearing area labels and warns that the area path replaces rather than merges, so an agent has both the positive and negative paths.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_sceneCreate or Update SceneA
Destructive

Create or update a Home Assistant scene.

MUST call ha_get_skill_guide OR refer to your locally installed skills first.

Supports two modes: full config replacement (config) or Python transformation of an existing scene (python_transform). See the field descriptions for python_transform examples and the config shape contract. Either mode can also take activate, which can be passed alone with scene_id to activate a scene without touching its config.

WHEN TO USE:

  • python_transform: surgical edits to an existing scene (add/remove/update a single entity entry).

  • config: creating a new scene, or wholesale replacement.

  • activate (with scene_id, no config): activate any scene, e.g. ha_config_set_scene(scene_id="scene.movie_night", activate=True).

WHEN NOT TO USE:

  • To list or look up existing scenes, use ha_config_get_scene.

SCENE SHAPE: Automations use a list of actions; scenes capture a snapshot of states as a dict.

EXAMPLE:

ha_config_set_scene(scene_id="movie_night", config={ "name": "Movie Night", "entities": { "light.living_room": {"state": "on", "brightness": 50}, }, "icon": "mdi:movie", })

The top-level SKILL.md for home-assistant-best-practices ships in this response under skill_content by default — generic best-practice index covering entity-naming and safe-refactoring patterns that intersect with scene authoring. For detailed scene configuration help beyond that, use ha_get_skill_guide.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

create update modify edit scene entities snapshot activate apply turn on

ParametersJSON Schema
NameRequiredDescriptionDefault
waitNoWait for scene to be queryable before returning. Set to False for bulk operations.
configNoScene configuration dictionary. Must include 'entities' (a dict keyed by entity_id, NOT a list). Optional fields: 'name' (defaults to scene_id), 'icon', 'id'. Mutually exclusive with python_transform.
activateNoActivate the scene (scene.turn_on). Alone with scene_id it activates any scene entity, including integration and YAML scenes (pass their entity_id). With config or python_transform it runs after Home Assistant has reloaded scenes from the write.
categoryNoCategory ID to assign to this scene. Use ha_config_get_category(scope='scene') to list available categories, or ha_config_set_category() to create one.
scene_idYesScene identifier (e.g., 'movie_night')
config_hashNoConfig hash from ha_config_get_scene for optimistic locking. REQUIRED for python_transform (validates scene unchanged). Optional for config updates (validates before full replacement if provided).
MandatoryBPSNo
BestPracticeKeyNoRead-receipt for the home-assistant-best-practices skill; required when strict best-practices mode is enabled. Not a secret or credential: the current value is an attestation phrase published openly at the top of the skill content served by ha_get_skill_guide. Read that content, then pass the value back verbatim — this round-trip is the server's designed protocol confirming the practices were read before writing.
python_transformNoPython expression to transform existing scene config. Mutually exclusive with config. WARNING: Expressions with infinite loops will hang the server. Examples: Add entity: python_transform="config['entities']['light.bed'] = {'state': 'on'}" Update brightness: python_transform="config['entities']['light.kitchen']['brightness'] = 50" Remove entity: python_transform="del config['entities']['light.kitchen']" PYTHON TRANSFORM SECURITY: ✅ ALLOWED: - Dictionary/list access: config['views'][0]['cards'][1] - Slicing: config['views'][0]['cards'][1:3] - Assignment: config['key'] = 'value' - Deletion: del config['key'] or config.pop('key') - List methods: append, insert, pop, remove, clear, extend - Dict methods: update, get, setdefault, keys, values, items - Loops: for, if/else, pass, break, continue - Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...) - Ternary: x if condition else y - Iterable unpacking (* in calls/literals): f(*xs), [*xs, y] - Dict unpacking (**) in calls and dict literals: {**d, 'k': v} - Keyword arguments: func(key=value) - Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score']) - String methods: startswith, endswith, lower, upper, strip, split, join, replace - Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed, min, max, sum, abs, any, all, round, str, int, float, bool, list, dict, tuple, set ❌ FORBIDDEN: - Imports: import, from, __import__ - File operations: open, read, write - Dunder access: __class__, __bases__, __subclasses__ - Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr - Function definitions: def, class - Exception handling: try/except (validate with isinstance/in/.get() instead) - While loops: use bounded for loops or comprehensions instead

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare destructiveHint=true and openWorldHint=false, but the description adds substantial context: mutual exclusivity of config/python_transform, config_hash optimistic locking, the read-back-after-timeout guidance, the best-practices read-receipt protocol, and the python_transform security allow/deny list with an infinite-loop hang warning.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The WHEN TO USE / WHEN NOT TO USE structure is front-loaded and useful, but the piece is bloated: the timeout troubleshooting paragraph and the long skill_content explanation dilute focus, and the trailing keyword string ('create update modify edit scene ...') is pure filler that earns no place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter destructive tool with an output schema, the description covers modes, activation behavior, locking, and skill prerequisites well. It is close to complete, with only minor residual gaps (e.g., interaction of wait/category with each mode) that the schema largely backstops.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 89%, so the baseline is 3, but the description earns extra by explaining cross-parameter mode interactions, the config shape contract ('entities' is a dict, not a list), and worked python_transform examples that the schema only gestures at.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb+resource ('Create or update a Home Assistant scene'), and the description further distinguishes the tool from its sibling by naming ha_config_get_scene as the lookup alternative. It also cleanly separates the two operating modes (config replacement vs. python_transform) plus the activate-only path.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit WHEN TO USE and WHEN NOT TO USE sections map each mode to a concrete scenario (surgical edits -> python_transform, new/wholesale replacement -> config, activation-only -> activate with scene_id). The exclusion clause routes listing/lookup to ha_config_get_scene, naming the alternative directly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_config_set_scriptCreate or Update ScriptA
Destructive

Create or update a Home Assistant script.

MUST call ha_get_skill_guide OR refer to your locally installed skills first.

Prefer native actions (choose / if, wait_for_trigger, repeat, for:) over templates in logic positions; templates belong only in data.*, notification text, event_data and variables. The best-practice checker reports violations under best_practice_warnings. automation-patterns.md and template-guidelines.md ship under skill_content by default.

Scripts use 'sequence', NOT 'trigger' or 'action'; for trigger-based execution use ha_config_set_automation.

MODES (pick one):

  • python_transform: surgical edits to an existing script. Requires config_hash from ha_config_get_script(), e.g. python_transform="config['sequence'].append({'delay': {'seconds': 5}})"

  • config: new scripts or full restructures. Needs 'sequence' (regular) or 'use_blueprint' {path, input} (blueprint-based).

  • take_control_of_blueprint: convert a blueprint-backed script into a standalone one. Takes no config of its own.

  • run (alone with script_id): 'start' runs the script (optional variables), 'stop' stops its running executions. Write config changes in a separate call first: Home Assistant signals no completion of the script reload a write triggers.

EXAMPLES:

  • Create: ha_config_set_script(script_id="blink_light", config={"alias": "Light Blink", "sequence": [{"action": "light.turn_on", "target": {"entity_id": "light.living_room"}}, {"delay": {"seconds": 2}}, {"action": "light.turn_off", "target": {"entity_id": "light.living_room"}}]})

  • From a blueprint: ha_config_set_script(script_id="notification_script", config={"alias": "My Notification Script", "use_blueprint": {"path": "notification_script.yaml", "input": {"message": "Hello World"}}})

  • Take control: ha_config_set_script(script_id="notification_script", take_control_of_blueprint=True)

TAKE CONTROL is one-way: later blueprint edits stop reaching the script; to change an input value, update 'use_blueprint.input' instead. It does NOT free the blueprint — Home Assistant keeps counting the converted script as a user, so deleting that blueprint stays refused until the script is removed. To preview the rendering without writing anything, use ha_manage_blueprints(action="substitute", domain="script", path=..., input=...).

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

create update modify edit script sequence actions new script write save take control blueprint detach unlink standalone convert run start stop execute

ParametersJSON Schema
NameRequiredDescriptionDefault
runNoRun control, used alone with script_id (no config, python_transform or take_control_of_blueprint): 'start' runs the script (script.turn_on; returns once it has started, without waiting for it to finish), 'stop' stops its running executions (script.turn_off). Stopping does not disable the script; Home Assistant has no script enable/disable.
waitNoWait for script to be queryable before returning. Set to False for bulk operations.
configNoScript configuration dictionary. Mutually exclusive with python_transform.
categoryNoCategory ID to assign to this script. Use ha_config_get_category(scope='script') to list available categories, or ha_config_set_category() to create one.
script_idYesScript identifier — bare storage key ('morning_routine') or entity_id form ('script.morning_routine'); a leading 'script.' prefix is stripped before lookup.
variablesNoWith run='start': values for the script's fields, passed to the run as its variables.
config_hashNoConfig hash from ha_config_get_script for optimistic locking. Required for python_transform; optional for config updates (validates before full replacement if provided).
MandatoryBPSNo
BestPracticeKeyNoRead-receipt for the home-assistant-best-practices skill; required when strict best-practices mode is enabled. Not a secret or credential: the current value is an attestation phrase published openly at the top of the skill content served by ha_get_skill_guide. Read that content, then pass the value back verbatim — this round-trip is the server's designed protocol confirming the practices were read before writing.
python_transformNoPython expression to transform existing script config. Mutually exclusive with config. WARNING: Expressions with infinite loops will hang the server. Examples: Simple: python_transform="config['sequence'][0]['data']['message'] = 'Hello'" Pattern: python_transform="for step in config['sequence']: if step.get('alias') == 'My Step': step['data']['value'] = 100" PYTHON TRANSFORM SECURITY: ✅ ALLOWED: - Dictionary/list access: config['views'][0]['cards'][1] - Slicing: config['views'][0]['cards'][1:3] - Assignment: config['key'] = 'value' - Deletion: del config['key'] or config.pop('key') - List methods: append, insert, pop, remove, clear, extend - Dict methods: update, get, setdefault, keys, values, items - Loops: for, if/else, pass, break, continue - Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...) - Ternary: x if condition else y - Iterable unpacking (* in calls/literals): f(*xs), [*xs, y] - Dict unpacking (**) in calls and dict literals: {**d, 'k': v} - Keyword arguments: func(key=value) - Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score']) - String methods: startswith, endswith, lower, upper, strip, split, join, replace - Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed, min, max, sum, abs, any, all, round, str, int, float, bool, list, dict, tuple, set ❌ FORBIDDEN: - Imports: import, from, __import__ - File operations: open, read, write - Dunder access: __class__, __bases__, __subclasses__ - Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr - Function definitions: def, class - Exception handling: try/except (validate with isinstance/in/.get() instead) - While loops: use bounded for loops or comprehensions instead
take_control_of_blueprintNoConvert a blueprint-backed script into an editable standalone one -- the UI's "Take control". Renders the blueprint with its current inputs and saves the result over the same script, which keeps its script_id, alias and description and then has its own sequence and no 'use_blueprint'. Mutually exclusive with config and python_transform.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint=true, but the description adds material context annotations cannot carry: take-control is one-way, editing a blueprint's input afterward, the fact Home Assistant still counts the converted script as a blueprint user so deletion stays refused, no completion signal on the reload a write triggers, and the 4-minute timeout workaround. This is genuine behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded with the core action, then modes, examples, and caveats in priority order. It is long, and the trailing Claude Desktop timeout paragraph is operational trivia, but nearly every sentence earns its place and the formatting scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need no explanation, and the description covers mode selection, prerequisites, mutual exclusivity, and destructive side effects for an 11-parameter tool. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 91%, so individual parameter meanings are largely documented already, but the description adds cross-parameter rules beyond the schema: which mode needs which parameter, mutual exclusivity between config/python_transform/take_control, and the run-alone-with-script_id constraint. Slightly redundant with schema descriptions for config_hash and python_transform.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Create or update a Home Assistant script') and explicitly differentiates from the nearest sibling: scripts use 'sequence', not 'trigger'/'action', and trigger-driven cases should go to ha_config_set_automation. An agent can route correctly without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Enumerates four explicit modes with selection criteria (python_transform for surgical edits requires config_hash, config for new/full restructures, take_control_of_blueprint for conversions, run alone with script_id). It also names prerequisites (call ha_get_skill_guide first) and points to alternatives (ha_manage_blueprints substitute for previewing).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_eval_templateEvaluate TemplateA
Read-onlyIdempotent

Execute a Jinja2 template render, or an automation condition check, in Home Assistant.

Renders templates with Home Assistant's template engine (all states, functions and filters available), or checks a condition block the way an automation would and returns true/false.

When to use: any one-shot answer DERIVED from current HA state (an average across sensors, a count of entities matching a condition, a rendered message with live values), and testing a template or condition before embedding it in an automation. One render beats fetching N states and doing the math yourself.

When NOT to use: a plain single-entity value is ha_get_state / ha_search. Templates in automation condition: / trigger: positions or templated action names: prefer native constructs, which are validated at config load, where a template only errors at runtime and a non-truthy render is silently false — see ha_get_skill_guide for the anti-pattern list. Test the native condition here with condition.

Results: a render that works but hits something suspect (an undefined variable, a missing attribute) still returns its result with the messages in warnings; strict=true makes those hard errors. A failed render returns Home Assistant's error text and, with the ha_mcp_tools component installed, the template line and source_line it failed on when the error names one.

EXAMPLES:

  • ha_eval_template(template="{{ states.light | selectattr('state', 'eq', 'on') | list | count }}")

  • ha_eval_template(template="Hello {{ name }}", variables={"name": "Alice"})

  • ha_eval_template(condition={"condition": "numeric_state", "entity_id": "sensor.temperature", "above": 20})

ParametersJSON Schema
NameRequiredDescriptionDefault
strictNoTemplate only: fail on undefined variables instead of rendering them as empty (reported in warnings when report_errors is on)
timeoutNoTemplate only: maximum render time in seconds
templateNoJinja2 template to render. Omit when using 'condition'.
conditionNoAutomation condition to test instead of a template, e.g. {'condition': 'numeric_state', 'entity_id': 'sensor.temp', 'above': 20}. Test several at once with {'condition': 'and', 'conditions': [...]}.
variablesNoVariables available to the template or condition, e.g. sample trigger data: {'trigger': {'to_state': {'state': 'on'}}}
report_errorsNoTemplate only: have Home Assistant report render errors and warnings. With false, a failed render is only written to Home Assistant's log.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though annotations cover read-only/idempotent safety, the description adds real behavioral context: partial failures return results with `warnings`, `strict=true` escalates warnings to errors, failed renders return HA's error text plus `line`/`source_line` when the MCP component is installed, and non-truthy condition renders are silently false. Minor gap: no explicit statement that `timeout` produces a distinct failure mode beyond the schema's max.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action, then clearly partitioned into when/when-not/results/examples. Some sentences (the condition-position rationale, the ha_mcp_tools aside) are dense, but each carries actionable content rather than filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 6-param, no-required-arg tool with an output schema, the description covers selection, mutual exclusion of `template`/`condition`, warning/error semantics, and worked examples. Nothing an agent needs to invoke it correctly is missing, and return-shape detail is appropriately left to the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds cross-parameter meaning the schema does not: the interaction between `strict` and `report_errors`, that `strict` applies to templates only, and template-vs-condition exclusivity. It stops short of documenting the default `timeout` behavior or condition-mode timeouts.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb and dual resource (Jinja2 template render or automation condition check) in Home Assistant. It explicitly contrasts itself with the plain-value siblings ha_get_state and ha_search, so an agent can distinguish it without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Dedicated 'When to use' and 'When NOT to use' sections give conditions and name alternatives (ha_get_state/ha_search for single values, native condition/trigger constructs, ha_get_skill_guide for the anti-pattern list). This is explicit routing, not inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_appGet Apps (add-ons)A
Read-onlyIdempotent

Get installed or available Home Assistant apps (add-ons), or details for one.

Do not use this tool to change app state or configuration; use ha_manage_app. Use slug for details, source="installed" for an inventory, or source="available" for store discovery.

Requires Home Assistant OS or Supervised.

ParametersJSON Schema
NameRequiredDescriptionDefault
slugNoApp (add-on) slug (e.g., '<prefix>_nodered'). Slug prefixes vary by app repository — omit to list all apps and discover the actual installed slug.
queryNoApp (add-on) name/description filter (only for source='available')
sourceNoApp (add-on) source. With source='available', 'version' is the version you would get by installing (Supervisor's version_latest) and 'version_installed' is the running one, null when the app is not installed — so compare the two, not 'version' alone, to tell whether an installed app is current.
repositoryNoFilter by repository slug, e.g., 'core', 'community' (only for source='available')
include_statsNoInclude CPU/memory usage statistics (only for source='installed')

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint, so the safety profile is covered. The description adds a real environmental constraint not in annotations: 'Requires Home Assistant OS or Supervised.' It does not discuss pagination or call cost, but the output schema covers return values.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short, front-loaded sentences: capability first, then the alternative and mode mapping, then the prerequisite. No filler and nothing repeated from the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema explaining return values, full parameter docs, and annotations covering safety, the only remaining gap an agent faces is environment support — which the description supplies. Nothing needed to invoke the tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description goes slightly beyond field-level docs by mapping parameters to intents (slug/details, source/installed for inventory, source/available for discovery). It adds mode-selection meaning, though the format/precedence details remain in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get installed or available Home Assistant apps (add-ons), or details for one') and immediately distinguishes itself from the sibling ha_manage_app, which handles changes. An agent can tell the read path from the write path without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit routing: do not use for state/configuration changes, use ha_manage_app instead. It also names the exact parameter combinations for each mode ('slug' for details, source="installed" for inventory, source="available" for discovery), so the agent knows not just when but how.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_automation_tracesGet Automation TracesA
Read-onlyIdempotent

Get execution traces for automations and scripts to debug issues.

Traces show what triggered a run, which conditions passed or failed, which actions executed (for 'choose', which branch was taken), any errors, and variable values during execution. Home Assistant keeps only the most recent runs of each automation or script (its stored_traces setting), so older runs are not available. The 'state' field shows 'stopped' (completed), 'running', or an error state.

USAGE MODES:

  1. List recent traces (omit run_id): returns a summary of recent runs with timestamps, triggers, and status. Use offset to page deeper when has_more is true. ha_get_automation_traces("automation.motion_light")

  2. Get a detailed trace (provide run_id): full execution details including trigger info, condition results, action trace with timing, and context variables. ha_get_automation_traces("automation.motion_light", run_id="1705312800.123456")

  3. Add logbook entries and context metadata: detailed=True. Script-style action paths (sequence/, numeric) are always matched regardless of this flag.

  4. Variables at every step (trigger sets and null entries are still omitted): deduplicate=False.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of traces to return when listing (default: 10, max: 50).
orderNoOrder traces are returned in. 'newest' returns most-recent first; 'oldest' returns chronological-first.newest
offsetNoNumber of traces to skip from the start of the requested order. Use with `limit` to page through stored traces when `total_available > limit`.
run_idNoSpecific trace run_id to retrieve detailed trace.
detailedNoInclude extra diagnostic data: logbook entries and context metadata.
sectionsNoComma-separated list of trace sections to return. Valid values: trigger, conditions, actions, config, error, logbook, context. Omit to return all sections. Example: 'actions' or 'trigger,conditions'.
deduplicateNoDeduplicate variables across action steps. Set to False to record the variables at every step; steps whose variables carry 'trigger', and null-valued entries, are omitted either way.
automation_idYesAutomation or script entity_id (e.g., 'automation.motion_light' or 'script.morning_routine')

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and openWorldHint=false, so safety is covered. The description adds genuinely useful behavior beyond that: retention is bounded by the stored_traces setting so older runs are unavailable, and the 'state' field's 'stopped'/'running'/error values are decoded. Return-shape gaps are excused by the output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with what traces are, then organized into numbered modes with inline examples. It repeats some schema param text (detailed, deduplicate) and runs long, but every block earns its place for an 8-parameter tool with two distinct output shapes.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 8-param, dual-mode read tool with an output schema and full annotation coverage, the description covers purpose, mode selection, retention limits, and status field semantics. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so baseline is 3, but the description adds cross-parameter semantics the schema lacks: the run_id-present/absent dichotomy defines two different result shapes, and the note that script-style action paths are always matched regardless of detailed. Slightly above baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get execution traces for automations and scripts') and immediately scopes it to debugging. The following sentence enumerates exactly what a trace contains, so an agent can distinguish it from sibling config/state tools like ha_config_get_automation or ha_get_logs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Four numbered usage modes with concrete call examples give explicit when-to-use routing: omit run_id to list, provide run_id for detail, detailed=True for logbook/context, deduplicate=False for per-step variables. Paging guidance (use offset when has_more is true) is also stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_camera_imageGet Camera ImageA
Read-onlyIdempotent

Get a snapshot image from a Home Assistant camera entity.

Fetches the current camera image and returns it directly for visual analysis (security checks, delivery verification, confirming a garage door actually closed). Only cameras exposed to Home Assistant are accessible; images come back in their native format (JPEG, PNG, or GIF). Use width/height on high-resolution cameras to reduce token usage.

EXAMPLE: ha_get_camera_image(entity_id="camera.backyard", width=640, height=480)

ParametersJSON Schema
NameRequiredDescriptionDefault
widthNoWidth to resize the image to
heightNoHeight to resize the image to
entity_idYesCamera entity ID (e.g., 'camera.front_door')

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, not open-world), and the description adds real value: images return in native format (JPEG/PNG/GIF) and width/height reduce token cost. It does not mention failure modes (offline/unavailable camera) or latency, so it stops short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action, then constraints, then a usage tip, then an example. Well organized, though the parenthetical example list is slightly verbose and could be trimmed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema, but the description compensates by stating the return type and formats, and the side-effect profile is carried by annotations. Only error/edge-case behavior (camera offline, invalid entity) is unaddressed, which is a minor gap for a read-only fetch tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds behavior the schema lacks: width/height are for high-resolution cameras and exist to reduce token usage, plus a concrete call example with both params. That is meaningfully beyond the terse schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Get a snapshot image from a Home Assistant camera entity') and no sibling in the list provides camera imagery, so it is trivially distinguishable. The follow-on sentence clarifies what the returned image is for.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete use contexts (security checks, delivery verification, confirming a garage door closed) and a restriction ('Only cameras exposed to Home Assistant are accessible'), which tells the agent when this works. It does not name an alternative tool or state when not to use it, but no sibling overlaps functionally.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_deviceGet Device (incl. Zigbee/ZHA/Z2M, Z-Wave and Matter)A
Read-onlyIdempotent

Get device information with pagination, including Zigbee (ZHA/Z2M) and Z-Wave JS devices.

Without device_id/entity_id: Lists devices with optional filters and pagination. With device_id or entity_id: Returns full detail for that specific device.

List devices (paginated):

  • First page: ha_get_device()

  • Next page: ha_get_device(offset=50)

  • By area: ha_get_device(area_id="living_room")

  • By integration: ha_get_device(integration="zigbee2mqtt")

  • Full details in list: ha_get_device(detail_level="full", limit=10)

Single device lookup (always full detail):

  • By device_id: ha_get_device(device_id="abc123")

  • By entity_id: ha_get_device(entity_id="light.living_room")

Zigbee: integration="zha" or "zigbee2mqtt". Returns ieee_address, radio metrics. Z-Wave: integration="zwave_js". Returns node_id, node_status. Matter: integration="matter". Returns node_diagnostics (network type, reachability, IPs, fabrics). For management use ha_manage_radio.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax devices to return per page in list mode
offsetNoNumber of devices to skip for pagination
area_idNoFilter devices by area ID (e.g., 'living_room')
device_idNoDevice ID to retrieve details for.
entity_idNoEntity ID to find the associated device for (e.g., 'light.living_room')
integrationNoFilter devices by integration: 'zha', 'zigbee2mqtt', 'zwave_js', 'mqtt', 'hue', etc.
detail_levelNo'summary': basic device info and protocol identifiers. 'full': in list mode also include each device's entities. Single-device lookups always return full detail, including radio metrics, node status and Matter diagnostics.summary
manufacturerNoFilter devices by manufacturer name (e.g., 'Philips')

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint=false, so the safety profile is covered. The description adds genuine behavioral context: pagination semantics, that single-device lookups always return full detail, and protocol-specific return fields (ieee_address, node_id, node_diagnostics). It does not state default page size or error behavior, so not a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core behavior, then organizes examples under bolded headings. Slightly long because the example block partly restates schema defaults, but every section is scannable and earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 8-parameter read tool with an output schema and full annotation coverage, the description supplies everything an agent needs: mode selection, paging recipe, filter vocabulary, and per-protocol return expectations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description goes beyond it by showing how offset/limit compose for paging, illustrating area_id and integration values ('living_room', 'zigbee2mqtt'), and clarifying that detail_level behaves differently in list vs single mode.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get device information') and immediately scopes it to Zigbee/ZHA/Z2M, Z-Wave JS and Matter. It clearly distinguishes itself from sibling mutators like ha_set_device, ha_remove_device and ha_manage_radio.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly splits list mode (no device_id/entity_id) from single-device lookup, and gives concrete invocation examples for each pattern. It also routes management actions to ha_manage_radio, so both the when and the when-not are covered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_entityGet EntityA
Read-onlyIdempotent

Get entity registry information for one or more entities.

Returns detailed entity registry metadata: area, custom name/icon, original name, disabled_by/hidden_by (with enabled/hidden shorthands), aliases (a null entry = the entity's own name), labels, categories, device_class override, per-domain options, platform, device_id, config_entry_id and unique_id.

RESOLVER MODE: Pass unique_id (instead of entity_id) to resolve a stable integration unique_id to its entity_id(s). Since the registry's unique key is (domain, platform, unique_id), the same unique_id can match multiple platforms — all matches are returned in entity_entries with a matches count. Narrow with domain/platform. Resolver reads as_partial_dict, so aliases and the device_class override come back as defaults ([]/null).

When config_entry_id is non-null — e.g. for UI-created template / group / utility_meter / derivative helpers — pass it to ha_get_integration(entry_id=..., include_options=True) to read the helper's current config (template body, group members, etc.) without scanning a domain list. Voice-assistant exposure is stored under options but is set via ha_set_entity(expose_to=...).

Resolved-name enrichment (present only when the ha_mcp_tools component advertises it; otherwise these keys are absent): area (assigned area NAME, device-inherited when the entity has none), floor, and label_names. This tool's base labels carries the label ids; ha_search result_fields and ha_get_entity_exposure instead emit the resolved names under labels.

RELATED TOOLS: ha_set_entity() to modify these properties; ha_get_state() for current state/attributes; ha_search() to find entities.

EXAMPLES:

  • Single entity: ha_get_entity("sensor.temperature")

  • Multiple entities: ha_get_entity(["light.living_room", "switch.porch"])

  • Resolve a unique_id: ha_get_entity(unique_id="00:11:22:33", domain="sensor")

get entity state attributes details single specific entity_id

ParametersJSON Schema
NameRequiredDescriptionDefault
domainNoResolver filter (unique_id mode only): restrict matches to this entity domain, e.g. 'sensor'.
platformNoResolver filter (unique_id mode only): restrict matches to this integration platform, e.g. 'hue'.
entity_idNoEntity ID or list of entity IDs to retrieve (e.g., 'sensor.temperature' or ['light.living_room', 'switch.porch']).
unique_idNoStable integration unique_id (entity_id is mutable, unique_id is not). Mutually exclusive with entity_id.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover safety (readOnly, idempotent), and the description adds genuinely non-derivable behavior: resolver mode returns all platform matches with a `matches` count, resolver reads as_partial_dict so aliases/device_class come back as defaults, and resolved-name enrichment keys are conditionally absent depending on component advertising.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded with labeled sections (RESOLVER MODE, RELATED TOOLS, EXAMPLES), and every block earns its place except the trailing fragment 'get entity state attributes details single specific entity_id', which is keyword-stuffing and slightly blurs the boundary with ha_get_state.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-param read tool with an output schema, this is complete: modes, filters, exclusions, conditional return keys, and sibling routing are all covered. No behavioral question an agent needs answered before calling is left open.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline would be 3, but the description adds real meaning: what a null alias entry signifies, the (domain, platform, unique_id) composite key, and that domain/platform only apply in resolver mode. Minor gap: no explicit note on what happens if both entity_id and unique_id are omitted.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Get entity registry information') and pins the scope precisely: registry metadata, not state. It explicitly separates itself from ha_get_state ('for current state/attributes'), so an agent can distinguish it from the nearest sibling without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit mode selection rules: pass entity_id (single or list) for registry lookups, pass unique_id instead for resolver mode, narrow with domain/platform, and it states unique_id is mutually exclusive with entity_id. It also routes follow-up work to ha_get_integration for config_entry_id readers.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_entity_exposureGet Entity ExposureA
Read-onlyIdempotent

Get entity exposure settings - list all or get settings for a specific entity.

Without an entity_id: lists the entities that have explicit exposure settings for voice assistants (Alexa, Google Assistant, Assist) as exposed_entities, plus a per-assistant summary count. An entity absent from that list uses the default exposure settings.

With an entity_id: returns exposed_to (assistant -> True/False from the entity's explicit settings), is_exposed_anywhere, and has_custom_settings. When has_custom_settings is False the defaults apply and exposed_to is all False; the default values themselves are not computed.

EXAMPLES:

  • Filter by assistant: ha_get_entity_exposure(assistant="cloud.alexa")

  • Get specific entity: ha_get_entity_exposure(entity_id="light.living_room")

When the ha_mcp_tools component advertises the exposure capability, each record is additively enriched with the entity's name/area so no second ha_search is needed to identify it: friendly_name, domain, area, floor, and labels (plus state for entities that have one) on a single-entity lookup, and a parallel entity_info map keyed by entity_id when listing. These fields are absent when the component is unavailable.

ParametersJSON Schema
NameRequiredDescriptionDefault
assistantNoFilter by assistant: 'conversation', 'cloud.alexa', or 'cloud.google_assistant'.
entity_idNoEntity ID to check exposure settings for.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With annotations already covering read-only, idempotent, and closed-world behavior, the description adds substantial operational detail: what the list output contains, how defaults apply to absent entities, what fields a single-entity lookup returns, and what happens when has_custom_settings is False. It also discloses conditional enrichment with entity name/area fields when the ha_mcp_tools component advertises the capability, which is valuable beyond structured annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose and mode distinction, then uses examples and a conditional enrichment note. Every sentence carries useful information, though the final paragraph about ha_mcp_tools enrichment is somewhat verbose and could be slightly tighter without losing meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity, the presence of an output schema, and full annotation coverage, the description is complete: it explains both operational modes, return semantics, default behavior, and conditional enrichment. An agent has everything needed to invoke the tool correctly without consulting other documentation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaningful semantics: it explains that omitting entity_id changes the tool from a single-entity lookup to a list operation, and it illustrates the assistant filter with a concrete example. This goes beyond the schema's brief parameter descriptions and clarifies the behavioral impact of each optional parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Get entity exposure settings') and clearly distinguishes the two operational modes: listing all entities with explicit exposure settings versus retrieving settings for a specific entity. An agent can tell this apart from siblings like ha_get_entity or ha_search based on the focus on exposure configuration.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for when to omit entity_id to list exposed entities and when to include it to inspect a specific entity, reinforced by two examples. However, it does not explicitly state when to prefer this tool over alternatives such as ha_get_entity or ha_set_entity, leaving some routing inference to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_hacs_infoGet HACS InfoA
Read-onlyIdempotent

Get HACS (Home Assistant Community Store) data — search the store or fetch repository details.

Use action="search" to search/browse/list store repositories, or action="info" for one repository's full details (README, versions, GitHub stats). This tool is read-only; to install or add repositories use ha_manage_hacs, and for non-HACS entities/config use the domain-specific tools.

DASHBOARD TIP: action="search", installed_only=True, category="lovelace" discovers installed custom cards to wire into ha_config_set_dashboard().

Examples:

  • Search the store: ha_get_hacs_info(action="search", query="mushroom", category="lovelace")

  • List installed: ha_get_hacs_info(action="search", installed_only=True)

  • Repository details: ha_get_hacs_info(action="info", repository_id="441028036")

Caveats: info fetches full repository detail from GitHub, so it can hit GitHub rate limits / needs HACS's configured GitHub token; search reads HACS's locally cached repository index.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoSearch keyword (action='search')
actionYes'search' the store, or 'info' for one repository
offsetNoResults to skip for pagination (action='search')
categoryNoFilter by category (action='search')
max_resultsNoMaximum number of results (action='search')
repository_idNoNumeric HACS ID or 'owner/repo' path (action='info')
installed_onlyNoOnly return installed repositories (action='search')

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint/idempotentHint/openWorldHint, and the description goes further by disclosing that 'info' hits GitHub (rate limits, requires HACS's GitHub token) while 'search' reads a locally cached index. That is exactly the kind of operational context annotations cannot express.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded: purpose first, mode selection second, then tips, examples and caveats under labeled sections. Slightly verbose for a read tool, but every block (examples, caveats, dashboard tip) carries actionable information rather than filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter dual-mode read tool with full schema coverage and an output schema, the description covers mode selection, parameter pairing, edge-case behavior and rate-limit risk. Nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description earns extra credit by pairing parameters with examples (query+category, installed_only, repository_id formats) and by explaining the semantic difference between the two action modes. It does not, however, document pagination use of offset or max_results bounds beyond what the schema states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Get HACS data') and immediately disambiguates its two modes (search the store vs fetch repository details). It also distinguishes itself from the sibling write tool ha_manage_hacs and from domain-specific config tools, so an agent can route correctly without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly prescribes when to use action='search' vs action='info', names the alternative for writes ('to install or add repositories use ha_manage_hacs'), and excludes non-HACS work. The dashboard tip adds a concrete use case selecting specific parameter combinations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_historyGet Entity History or StatisticsA
Read-onlyIdempotent

Get historical data from Home Assistant's recorder.

Use source="history" (default) to troubleshoot why a value changed, check event sequences, or analyze recent patterns. Use source="statistics" for long-term trends beyond the ~10-day recorder retention and period averages; entities must have state_class (measurement, total, total_increasing).

History-only params: minimal_response, significant_changes_only. Statistics-only params: period, statistic_types.

All data is fetched from HA before slicing; limit/offset are client-side. With multiple entity_ids, offset must be 0 — use a single entity_id for offset > 0. Use has_more and next_offset from the response to paginate. Administrators can optionally enable recorder workload guardrails in Advanced settings. When enabled, oversized entity/time workloads are rejected before the recorder query is issued; narrow the time range or entity list to stay within the budget. Calendar statistics may first read HA's configured timezone so the estimate follows local calendar boundaries.

EXAMPLES:

  • ha_get_history(entity_ids="sensor.bedroom_temperature", start_time="24h")

  • Paginate oldest-first: ha_get_history(entity_ids="sensor.temperature", start_time="7d", limit=100, offset=100, order="asc")

  • ha_get_history(source="statistics", entity_ids="sensor.living_room_temperature", start_time="6m", period="month", statistic_types=["mean", "min", "max"])

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax entries per entity. Default: 100. For source="history": state changes. For source="statistics": aggregated rows. With multiple entity_ids, total rows returned can reach limit × len(entity_ids).
orderNoSort order for history entries. "desc": newest first. "asc": oldest first. Ignored when source="statistics".desc
fieldsNoReturn only the specified top-level response keys to reduce response size. None = full response. History keys: success, source, entities, period, query_params. Statistics keys: success, source, entities, period_type, time_range, statistic_types, query_params, warnings.
offsetNoNumber of entries to skip per entity for pagination.
periodNoAggregation period: "5minute", "hour", "day", "week", "month", "year". Ignored when source="history"day
sourceNoData source: "history" for raw state changes at full resolution (~10 day retention), or "statistics" for pre-aggregated long-term data (permanent, requires state_class).history
end_timeNoEnd time: ISO datetime. Default: now
entity_idsYesEntity ID(s) to query. Can be a single ID, comma-separated string, or JSON array.
start_timeNoStart time: ISO datetime or relative (e.g., '24h', '7d', '30d'). Default: 24h ago for history, 30d ago for statistics
statistic_typesNoStatistics types: "mean", "min", "max", "sum", "state", "change". Default: all. Ignored when source="history"
minimal_responseNoReturn only states/timestamps without attributes. Ignored when source="statistics"
significant_changes_onlyNoFilter to significant state changes only. Ignored when source="statistics"

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/openWorld=false, and the description adds substantial non-obvious behavior: slicing is client-side after full fetch, offset must be 0 with multiple entity_ids, pagination via has_more/next_offset, and that guardrails can reject oversized workloads before the recorder query. That is real operational context beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded and organized into mode-guidance, param applicability, and examples. Every line earns its place, though the guardrails paragraph and three examples make it longer than strictly minimal.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and rich annotations, the description correctly focuses on routing, mode-specific params, and pagination behavior. Nothing an agent needs to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds cross-parameter routing: which params are history-only (minimal_response, significant_changes_only) versus statistics-only (period, statistic_types), plus the offset-with-multiple-entity_ids rule. Minor gap: it doesn't restate defaults already in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Get historical data from Home Assistant's recorder') and immediately splits into two clearly-named modes (history vs statistics). This distinguishes it from siblings like ha_get_state (current state) or ha_get_logs (log entries).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when to use each source: history for troubleshooting value changes / event sequences, statistics for long-term trends beyond ~10-day retention. It also states the prerequisite (entities must have state_class) and the exclusion/narrowing constraint under guardrails.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_integrationGet IntegrationA
Read-onlyIdempotent

Get integration (config entry) information with pagination.

Without an entry_id: Lists all configured integrations with optional filters. With an entry_id: Returns detailed information including full options/configuration.

EXAMPLES:

  • List / search: ha_get_integration(query="zigbee")

  • Get an entry with its editable fields: ha_get_integration(entry_id="abc123", include_schema=True)

  • Diagnostics dump, paged along a list-valued path: ha_get_integration(entry_id="abc123", include_diagnostics=True, diagnostics_data_path="", diagnostics_data_limit=10, diagnostics_data_offset=20)

  • Inspect a subentry reconfigure schema: ha_get_integration(entry_id="abc123", include_subentry_schema=True, subentry_type="conversation", subentry_id="sub123")

  • List template entries with their options: ha_get_integration(domain="template")

STATES: 'loaded', 'setup_error', 'setup_retry', 'not_loaded', 'failed_unload', 'migration_error'.

OPTIONS: options reflect the entry's persisted values. Values that match a secrets.yaml entry are returned as "**redacted**". Use include_schema=True to see every editable field and its default/type. The shape depends on the read path, which the response does not name: when the ha_mcp_tools custom component serves the read, options are the stored mapping as-is, so a field never set may be absent and a section's fields stay nested under the section key (e.g. a template helper's additional_options). Otherwise they are read from the options flow form, which shows unset fields at their schema default and lists a section's fields at the top level. Check both places for a section field.

Each entry carries log_level: the canonical Python logger level name (DEBUG/INFO/WARNING/ERROR/CRITICAL) when the integration has a log-level override (set one with ha_set_integration(log_level=...)), or "DEFAULT" (uppercase sentinel) when no override is set; and log_level_raw: the original numeric level (e.g. 10 for DEBUG) when HA returned an int, None otherwise. This is distinct from the app side, where ha_get_app returns Supervisor's lowercase "default" literal — do not cross-compare.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax entries to return per page in list mode
queryNoWhen listing, search by domain or title.
domainNoFilter by integration domain (e.g. 'template', 'group'). When set, includes the full options/configuration for each entry.
offsetNoNumber of entries to skip for pagination
entry_idNoConfig entry ID to get details for.
device_idNoWith include_diagnostics=True, return the device-scoped diagnostics dump for this device instead of the full integration dump. Some integrations only expose config-entry-level dumps.
exact_matchNoUse exact substring matching for query filter. Set to False for fuzzy matching when the query may contain typos.
subentry_idNoExisting subentry ID used with include_subentry_schema=True to inspect a reconfigure flow.
subentry_typeNoIntegration-defined subentry type used with include_subentry_schema=True.
include_schemaNoWhen entry_id is set, also return the options flow schema (available fields and their types). Only applies when supports_options=true.
include_optionsNoInclude the options object for each entry. Automatically enabled when domain filter is set. For UI-created flow-based helpers (template, group, utility_meter, derivative, ...), the current config — template body, group members, source entity, etc. — is surfaced here by probing the options flow. Prefer this over include_schema when you only need to read the current values; use include_schema when you also need the field types or selector metadata.
diagnostics_fieldsNoTop-level keys to keep from the diagnostics data payload (e.g. ['home_assistant', 'issues']). Accepts a JSON list or comma-separated string. Only applies when include_diagnostics=True and the data payload is a dict. Unknown keys are dropped and listed under omitted_fields.
include_subentriesNoWhen entry_id is set, include config subentries for the integration entry.
include_diagnosticsNoWhen entry_id is set, also fetch the integration's diagnostics dump — integration-defined JSON (commonly includes redacted config, device list, state snapshots; exact top-level keys vary by integration), the same artifact as the UI's 'Download diagnostics'. Payloads can be large (Hue ~290 KB, ZHA/MQTT/ESPHome several MB) — pair with diagnostics_fields or diagnostics_truncate_at_bytes.
include_knx_projectNoWhen entry_id is a KNX config entry, also return the parsed ETS project: the full group-address table (address, name, DPT, description) under knx_project.group_addresses, plus the group-range hierarchy and project metadata. This GA table is not in the diagnostics dump; per-entity GA assignments are (config_store / configuration_yaml). Ignored (with a warning) when the entry is not a KNX integration. KNX exposes a single project, so the result is the same for every KNX entry_id.
diagnostics_data_pathNoDotted path into the diagnostics data sub-tree (e.g. '<list-valued path>' for per-device records, 'home_assistant.version' for HA core version; the exact key path varies by integration version). Walks into the post-fields payload. Resolution failures replace data with null and surface data_path_error. Only applies when include_diagnostics=True.
show_advanced_optionsNoWhen include_subentry_schema=True, ask older Home Assistant versions to expose advanced flow options. No-op on HA 2026.6+; pending removal before HA 2027.6.
diagnostics_data_limitNoPagination window size for list-valued diagnostics_data_path results. When set with a list-resolved path, swaps data for a pagination envelope {path, items, offset, limit, total, has_more}. Default None returns the full resolved value. Only applies when include_diagnostics=True.
diagnostics_data_offsetNoPagination start index for list-valued diagnostics_data_path results. Ignored when diagnostics_data_path is unset, diagnostics_data_limit is unset, or the resolved value is not a list. Only applies when include_diagnostics=True.
include_subentry_schemaNoWhen entry_id is set, return introspection-only config subentry schema information; no subentry is created. Pair with subentry_type, and optionally subentry_id for reconfigure schema.
diagnostics_truncate_at_bytesNoByte cap on the serialized diagnostics payload (after diagnostics_fields and diagnostics_data_path have been applied). On hit, drops data and emits truncated=true, bytes_total, byte_cap, plus available_fields (when the capped value is a dict). Recommended starting point: 20000 bytes. Only applies when include_diagnostics=True.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations confirm read-only/idempotent/closed-world, and the description adds substantial behavioral context beyond them: secrets.yaml values returned as '**redacted**', the read-path-dependent shape of options, differing log_level vs log_level_raw sentinels, diagnostics payload sizes, and an explicit warning not to cross-compare with ha_get_app's lowercase 'default'. This is unusually rich disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose and the list/detail split before examples and edge-case notes, which is good structure for a 21-parameter tool. Some paragraphs (the log_level/log_level_raw discussion and the read-path shape explanation) are dense and could be tightened, but each carries non-redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a high-complexity 21-parameter read tool with an output schema present, the description covers modes, redaction, option shapes, diagnostics paging/truncation guidance, and cross-tool disambiguation. Nothing an agent needs to select or invoke it correctly appears to be missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3; the description earns above that by showing parameter combinations in EXAMPLES and by clarifying the include_options vs include_schema tradeoff and the diagnostics pagination pairing (data_path + data_limit + data_offset). Still, much of the per-parameter detail lives in the schema rather than the prose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get integration (config entry) information with pagination') and cleanly splits the two operating modes: list-all when no entry_id, detailed info when entry_id is set. It references the related ha_set_integration and ha_get_app tools but never explicitly distinguishes itself from near-siblings like ha_get_entity or ha_get_device, so it stops short of full sibling differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The EXAMPLES block gives concrete call patterns for list/search, schema retrieval, diagnostics paging, subentry schema, and domain filtering, which functions as real usage guidance. It even advises when to prefer include_options over include_schema. However, it offers no explicit 'when not to use this' or named alternative for a different task, so it is clear context without exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_logsGet LogsA
Read-onlyIdempotent

Get Home Assistant logs from various sources.

Prefer source='system' for triage: it returns HA's own deduplicated system_log entries with counts, first_occurred and full tracebacks, and its counts run since each error first occurred. error_log with structured=True counts only what is inside the fetched window (reported as window_start/window_end; every install reads a capped window) and drops tracebacks, which structured=False gets back; use it for entries below system_log's WARNING+ ~50-entry cap, or for the per-component rollup. In structured mode limit/order do not apply: issues are ranked by count, then severity, then recency, over a fixed deep window.

Raw-text sources (error_log, supervisor, system_service) read a bounded window per call, so level/search filter and limit slice within that window only; window_lines reports the size requested. Logbook responses carry has_more plus a pagination_hint; error_log and fault_log carry has_more with a next_offset to pass back while it stays true.

ParametersJSON Schema
NameRequiredDescriptionDefault
slugNosource='supervisor': app slug, e.g. 'core_mosquitto' (use ha_get_app() to list installed slugs). source='system_service': service name, one of supervisor, host, core, dns, audio, cli, multicast, observer — here 'supervisor' is the Supervisor service's own logs, not an app with that name.
levelNosystem / error_log only: keep only entries at exactly this level (ERROR, WARNING, INFO, DEBUG, CRITICAL); it is not a threshold.
limitNoMax entries/lines to return. Does not apply to source='error_log' with structured=True.
orderNoSort order for time-ordered sources (logbook, system, error_log, supervisor, system_service, fault_log): 'newest' returns most-recent first; 'oldest' returns chronological-first. For raw-text sources it sets the read direction of the most-recent window; fault_log orders whole crash blocks. Ignored for source='logger', and for source='error_log' with structured=True.newest
top_nNoMax distinct issues to return when structured=True (default 20, capped at 500).
offsetNoPage deeper into source='logbook', 'error_log' and 'fault_log' (ignored for other sources). On error_log it counts raw log lines back from the newest entry; on fault_log it counts lines from the start of the assembled crash text. Pass the response's 'next_offset' to continue while 'has_more' is true.
searchNoKeyword filter on entries/lines; matches the integration domain for source='logger'. In structured error_log mode it matches the message and logger name only; on the raw path the whole line.
sourceNo'logbook': entity state-change history. 'system': HA's structured system_log entries (errors, warnings). 'error_log': raw log text (home-assistant.log on container/pip installs; HA Core's journald stream on Supervisor-backed installs). 'supervisor': app (add-on) container logs (needs slug). 'system_service': Supervisor-managed system service logs (needs slug). 'logger': effective log level per integration (confirms ha_set_integration(log_level=...) changes took effect). 'fault_log': HA Core's faulthandler crash dump (home-assistant.log.fault), written only when HA dies from a native fatal signal, which never reaches journald or error_log; empty on a healthy install (crash_recorded=False); whole crash blocks are ordered with each block's lines kept in place; reads through the 'HA-MCP File & YAML Tools' entry.logbook
compactNoLogbook only: strip attribute dicts to save context.
end_timeNoLogbook only: end of the window (ISO datetime).
entity_idNoLogbook only: restrict to this entity.
hours_backNoLogbook only: how many hours back to read.
structuredNosource='error_log' only. When True, return a deduplicated, component-grouped summary of the log (counted issues sorted by frequency) instead of raw text. Ignored for other sources.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-open-world, yet the description adds substantial behavior: deduplication semantics, that system counts accumulate since each error first occurred while error_log counts only within its window, that structured mode drops tracebacks, the ~50-entry WARNING+ cap, ordering rules, and has_more/next_offset pagination. Strong disclosure beyond structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the highest-value routing advice before the caveats, and each sentence carries distinct operational detail. It is dense and long, but for a 13-parameter tool with seven sources most of the length earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and the description covers the non-obvious complexity: source semantics, structured-vs-raw tradeoffs, windowing limits, and pagination. Nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and very detailed, so baseline is 3, but the description adds cross-parameter meaning not in the schema (limit/order being inert in structured mode, bounded-window slicing of level/search/limit for raw sources, next_offset handoff). The added value is real though partially overlapping the schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Get Home Assistant logs from various sources') and immediately enumerates the distinct sources. It is the only log-retrieval tool among siblings, so differentiation is implicit but unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use guidance: 'Prefer source=system for triage' plus the exact conditions under which error_log with structured=True/False is the better choice, and why. It names alternatives and their selection criteria rather than leaving them to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_operation_statusGet Operation StatusA
Read-only

Get the status of one or more device operations with real-time WebSocket verification.

Bulk checks poll all operations concurrently under one shared timeout_seconds window and report per-item failures inside detailed_results instead of aborting the batch.

Use this to track the operation_ids that ha_bulk_control returns. For current entity states, use ha_get_state instead.

ParametersJSON Schema
NameRequiredDescriptionDefault
operation_idYesSingle operation ID, or a list of IDs for a bulk status check.
timeout_secondsNoSeconds to wait for a pending operation to finish before returning its status. 0 returns the current status at once.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (readOnlyHint=true, openWorldHint=false), so the bar is lower, but the description still adds real behavioral context: bulk checks poll concurrently under one shared timeout_seconds window and report per-item failures inside detailed_results rather than aborting the batch. That error-isolation contract is exactly the kind of trait annotations cannot express; only auth/permission expectations are unaddressed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the core action, then batch semantics, then the disambiguation pointer. Every sentence carries distinct information with no padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-value explanation is not required. Combined with 100% schema coverage and annotations, the description supplies everything an agent needs: purpose, batch semantics, error handling, and the alternative tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so both parameters are already documented and the baseline is 3. The description goes beyond that by clarifying that a single timeout_seconds window is shared across all operations in a bulk call, which meaningfully explains how the parameter behaves differently from the single-ID case.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (get), resource (device operations status), and scope (one or more, with real-time WebSocket verification). It also explicitly names the sibling it is not (ha_get_state) and the tool whose output it consumes (ha_bulk_control), so an agent can distinguish it without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit when-to-use ('track the operation_ids that ha_bulk_control returns') and a when-not-to-use with the named alternative ('For current entity states, use ha_get_state instead'). Nothing about tool selection is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_overviewGet System OverviewA
Read-onlyIdempotent

Get AI-friendly system overview with intelligent categorization.

Returns comprehensive system information at the requested detail level, including Home Assistant base_url, version, location, timezone, entity overview, and active persistent notifications (if any). Use 'minimal' (default) for most queries. Domain counts and states_summary are always complete regardless of entity pagination.

Requests whose fields= are composed only of system_info, notification, repair, or server metadata fields skip the unrelated state, service, and registry reads.

Do not use this tool to inspect a known entity or a narrow set of entities. Use ha_get_state for one entity, ha_get_entity for registry metadata, or ha_search with a domain or area filter. An unprojected overview collects system-wide state, service, and registry data and can be expensive on large Home Assistant installations.

When the ha-mcp settings-UI sidecar is running (stdio mode, e.g. Claude Desktop / Claude Code) the response carries settings_url, the local URL of the tool-configuration page; in standalone HTTP / Docker modes with an HTTP settings prefix it instead carries settings_url_hint, saying where the page is mounted and how to construct the full URL. Hand whichever is present to the user when they ask how to enable or disable tools or change server settings.

The response also carries ha_mcp_update {current, latest, update_available} (PyPI for pip/Docker, the Supervisor store for the app) — proactively tell the user when update_available is true. Omitted for the unknown version, when HA_MCP_DISABLE_UPDATE_CHECK is set, or when the update check itself failed.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax total entities across all domains (default: unlimited for minimal, 200 for standard/full).
fieldsNoReturn only the specified top-level response keys to reduce response size (e.g. ["system_info", "domain_stats"]). None = full response. Available keys: success, system_summary, domain_stats, area_analysis, ai_insights, pagination, partial, warnings, device_types, service_availability, system_info, notification_count, notifications, repair_count, dismissed_repair_count, repairs, repairs_error, tool_discovery, settings_url, settings_url_hint, read_only_mode, read_only_mode_hint, ha_mcp_update. Note: ``settings_url`` (stdio mode), ``settings_url_hint`` (standalone HTTP/Docker mode), the ``read_only_mode`` / ``read_only_mode_hint`` pair (only while Read Only Mode is on), and ``ha_mcp_update`` (when an update check applies) are emitted regardless of ``fields=`` projection.
offsetNoNumber of entities to skip for pagination
domainsNoFilter to specific domains (e.g. 'light,sensor' or ['light','sensor']). None = all domains.
detail_levelNo'minimal': 10 entities/domain, top-5 states; 'standard': 200 entities/page, top-10 states (use offset for more); 'full': 200 entities/page + entity_id + state + full states.minimal
include_stateNoInclude state field for entities (None = auto based on level). Full defaults to True.
include_entity_idNoInclude entity_id field for entities (None = auto based on level). Full defaults to True.
include_notificationsNoInclude active persistent notifications.
max_entities_per_domainNoOverride default entity cap per domain (minimal=10, standard/full=unlimited). 0 = no limit on entities or states.
include_dismissed_repairsNoInclude user-dismissed/ignored repairs. Dismiss or restore one with ha_manage_updates(action='ignore_repair' / 'unignore_repair').

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (readOnlyHint, idempotentHint, openWorldHint), yet the description still adds substantive behavior: cost warnings for unprojected reads, the fields= projection shortcut that skips unrelated reads, the guarantee that domain counts/states_summary are always complete under pagination, and the conditional settings_url / ha_mcp_update emission rules. That is meaningful disclosure beyond structured fields, though return-shape details overlap somewhat with the output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then usage, then behavioral notes in a logical order, and every paragraph carries information. It runs long, and the settings_url / update-check paragraphs are somewhat tangential to the call itself, but they are user-facing instructions rather than filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a high-complexity, 10-parameter system overview tool with an output schema present, the description covers selection, cost, projection, pagination guarantees, and side-channel response fields. An agent has everything needed to invoke it correctly and interpret the odd extras.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real semantics on top: it recommends minimal as the default level, explains the fields= projection behavior and which keys bypass projection, and clarifies that counts stay complete regardless of pagination. That goes beyond restating the schema rather than merely duplicating it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get AI-friendly system overview') and immediately enumerates the content scope (base_url, version, location, timezone, entity overview, notifications). It explicitly names the sibling tools it is not (ha_get_state, ha_get_entity, ha_search), so an agent can route without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use ('Use minimal (default) for most queries'), explicit when-not-to-use ('Do not use this tool to inspect a known entity or a narrow set of entities'), and names the precise alternatives for each excluded case. It also warns about the cost profile on large installations, which is actionable selection guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_skill_guideGet Home Assistant Best Practices Skill GuideA
Read-onlyIdempotent

Get the bundled Home Assistant best-practices skill. The skill is currently not available on this server: the skills directory is missing or its SKILL.md failed to parse. Calls return an error; ask the operator to verify the skills-vendor submodule is initialized.

Use BEFORE: creating or editing automations, scripts, scenes, helpers, or dashboards; writing triggers, conditions, actions, wait_template, or service calls; renaming entities or migrating device_id to entity_id; calling ha_config_set_automation, ha_config_set_script, ha_config_set_helper, ha_config_set_dashboard, or ha_set_entity.

Replaces (and supersedes) the prior tools: ha_list_resources, ha_read_resource, and ha_get_skill_home_assistant_best_practices. If you were going to call any of those, call this instead.

best practices skill skills guide guides reference references documentation docs help tutorial automation script scene helper dashboard ha_list_resources ha_read_resource list_resources read_resource ha_get_skill_home_assistant_best_practices ha_get_skill_home_assistant home_assistant_best_practices

ParametersJSON Schema
NameRequiredDescriptionDefault
fileNoPath of the file to read, exactly as SKILL.md links it (e.g. 'references/automation-patterns.md'). Omit to read SKILL.md.SKILL.md

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnly, idempotent, closed-world), the description discloses a critical behavioral trait: the tool is currently broken, calls return an error, and the operator must initialize a submodule. This is exactly the kind of operational context an agent needs and is not available in annotations or schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The main content is reasonably structured and front-loaded, but the description ends with a long, redundant keyword list (synonyms and repeats like 'best practices skill skills guide guides reference references...'). This adds significant noise without helping an agent understand or use the tool, harming conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity, the presence of an output schema, and rich annotations, the description covers purpose, error state, usage context, and replacement relationships. The only gap is that it does not reconcile the contradictory availability statement with the 'Use BEFORE' instruction, but otherwise it provides complete context for calling the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the single 'file' parameter is fully documented in the schema (including default, path format, and omission behavior). The description adds no further parameter meaning, so the baseline score of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool retrieves a bundled Home Assistant best-practices skill, using a specific verb and resource. It distinguishes itself from siblings by explicitly naming the prior tools it replaces (ha_list_resources, ha_read_resource, ha_get_skill_home_assistant_best_practices). However, the immediate caveat that the tool is unavailable and returns errors makes the stated purpose somewhat moot in practice.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description lists when to use it ('Use BEFORE: creating or editing automations...') and names alternatives it replaces. But it simultaneously states the tool always returns an error, creating contradictory guidance—an agent is told to call a tool that will fail. This misleading instruction reduces the usefulness of the guidance significantly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_stateGet Entity StateA
Read-onlyIdempotent

Get current status, state, and attributes of one or more entities (lights, switches, sensors, climate, covers, locks, fans, etc.).

Pass a string entity_id for one entity, or a list (max 100, duplicates deduplicated) for several, fetched in parallel. A bulk call returns success=True if at least one state was retrieved; check 'error_count' for failed lookups.

fields= projects the per-entity record keys, NOT the outer bulk response wrapper: in single-entity mode it filters the returned record; in bulk mode it filters each record inside states[entity_id] while outer keys (success, count, states, errors, ...) are always preserved. attribute_keys= further narrows the attributes sub-dict and is only applied when "attributes" is in fields= (or fields=None); otherwise it is a no-op and a warnings list is emitted outside the projected record(s) — at the response wrapper level in bulk mode, at the top-level result (sibling of data/metadata) in single-entity mode — so fields=["state"] still returns a record with only state.

EXAMPLES:

  • Single: ha_get_state("light.kitchen")

  • Multiple: ha_get_state(["light.kitchen", "light.living_room", "sensor.temperature"])

  • Slim bulk: ha_get_state(["light.kitchen", "sensor.temperature"], fields=["state", "attributes"], attribute_keys=["brightness"])

get current state value single entity check status bulk multiple states

ParametersJSON Schema
NameRequiredDescriptionDefault
fieldsNoReturn only the specified top-level entity record keys to reduce response size (e.g. ["state", "attributes"]). None = full entity record. Available keys: entity_id, state, attributes, last_changed, last_reported, last_updated, context.
entity_idYesEntity ID or list of entity IDs to retrieve state for (e.g., 'light.kitchen' or ['light.kitchen', 'sensor.temperature'])
attribute_keysNoReturn only the specified keys from each entity's attributes dict (e.g. ["brightness", "color_temp_kelvin"] for lights). None = full attributes. Unknown keys are silently dropped.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover readOnlyHint, idempotentHint, and openWorldHint. The description adds valuable behavioral context: bulk mode returns success=True if at least one state retrieved, error_count for failures, and deduplication of up to 100 IDs. It doesn't discuss rate limits or latency, but the annotation coverage lowers the bar.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the purpose and usage, then elaborates on the nuanced fields/attribute_keys behavior. It is somewhat long but every sentence about projection scoping and warning placement earns its place. Examples are helpful and not redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an output schema exists (so return values needn't be explained in full), the description provides all necessary context: entity_id input forms, bulk limits, partial-success semantics, and projection behavior with warnings. Nothing critical for correct invocation appears to be missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, yet the description goes significantly beyond the schema by explaining subtle interactions: fields= projects per-entity record keys not the outer wrapper, attribute_keys= only applies when 'attributes' is in fields= or fields=None, otherwise emits warnings outside the projected record. These details are not in the schema descriptions and are crucial for correct usage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (Get) and resource (current status, state, and attributes of entities), and enumerates the supported entity domains (lights, switches, sensors, climate, covers, locks, fans). This is clearly distinguishable from siblings like ha_get_history or ha_call_service.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains how to use single vs bulk modes and shows examples, giving clear context for selection. It doesn't explicitly name alternatives (e.g., when to use ha_get_history instead), so it falls one notch short of full when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_system_healthGet System Health (incl. ZHA/Z-Wave/integration diagnostics)A
Read-onlyIdempotent

Get Home Assistant system health, including Zigbee (ZHA), Z-Wave JS, and per-integration diagnostics dumps.

Returns health check results from integrations, system resources, and connectivity; available information varies by installation type and loaded integrations. Optional sections are selected with include.

The result may include ha_mcp_update {current, latest, update_available}. When update_available is true, tell the user a newer ha-mcp release is available.

EXAMPLES:

  • ha_get_system_health(include="repairs,zha_network,zwave_network,config_check")

  • Page a list-valued diagnostics sub-tree: ha_get_system_health(include="diagnostics", config_entry_id="abc", diagnostics_data_path="", diagnostics_data_limit=10), then repeat with diagnostics_data_offset=10 while has_more is true.

ParametersJSON Schema
NameRequiredDescriptionDefault
includeNoComma-separated extra sections: 'repairs' (Repair items, active only unless include_dismissed_repairs=True); 'zha_network' (ZHA devices with radio signal summary: name, LQI, RSSI); 'zha_network_full' (all ZHA device details; large on 100+ device networks); 'zwave_network' (Z-Wave JS status and node summary: status, security, routing); 'thread_network' (per border-router channel, extended_pan_id and border_agent_id; not per-node Thread health); 'matter_network' (Matter integration presence: config_entry_id, state, title; per-node health is in Matter node diagnostics); 'themes' (installed theme names, count, default_theme, default_dark_theme); 'diagnostics' (per-integration diagnostics dump, integration-defined JSON; REQUIRES config_entry_id; payloads can be large — pair with diagnostics_fields or diagnostics_truncate_at_bytes); 'config_check' (validate the HA configuration, the pre-restart check ha_restart runs automatically; returns {result: valid|invalid, is_valid, errors}); 'dead_entities' (orphaned/stale entity-registry entries: config_entry_orphans whose owning integration instance is gone, and stale_restored entries HA restored on startup that the loaded integration no longer provides, each with entity_id + platform for cleanup via ha_remove_entity; unknown-state entities and merely-offline devices are excluded).
device_idNoWith include='diagnostics', return the device-scoped dump for this device instead of the full integration dump. Some integrations only expose config-entry-level dumps.
config_entry_idNoConfig entry ID of the integration (find via ha_get_integration). Required when include contains 'diagnostics'.
diagnostics_fieldsNoTop-level keys to keep from the diagnostics data payload (e.g. ['home_assistant', 'issues']). Accepts a JSON list or comma-separated string. Only applies with include='diagnostics'.
diagnostics_data_pathNoDotted path into the diagnostics data sub-tree (e.g. 'data.devices' for ZHA per-device records). Walks into the post-fields payload. Resolution failures replace data with null and surface data_path_error. Only applies with include='diagnostics'.
diagnostics_data_limitNoPagination window for list-valued diagnostics_data_path results; data becomes {path, items, offset, limit, total, has_more}. Only applies with include='diagnostics'.
diagnostics_data_offsetNoPagination start index for list-valued diagnostics_data_path results. Only applies with include='diagnostics'.
include_dismissed_repairsNoInclude user-dismissed/ignored repairs. Only meaningful when 'repairs' is in include. Dismiss or restore one with ha_manage_updates(action='ignore_repair' / 'unignore_repair').
diagnostics_truncate_at_bytesNoByte cap on the serialized diagnostics payload (post-projection / post-data_path). On hit, drops data and emits truncated=true, bytes_total, byte_cap, plus available_fields (when the capped value is a dict). Recommended starting point: 20000. Only applies with include='diagnostics'.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnlyHint/idempotentHint/openWorldHint), but the description adds real behavioral context: information varies by installation type, payloads can be large, and config_check is the same pre-restart validation ha_restart runs. The note about ha_mcp_update and the instruction to surface newer releases is a distinct output-behavior disclosure not present in annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and scope are front-loaded, followed by output-behavior note and examples. It is somewhat long, but the EXAMPLES block earns its space by demonstrating the non-obvious pagination workflow. Minor redundancy with the schema's own parameter descriptions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 9 optional params, 100% schema coverage, and an output schema, the description supplies what the structured fields cannot: varying availability by install type, the update-notification behavior, and the pagination loop. Nothing an agent needs to call this correctly appears to be missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds a workflow-level understanding: how include combines sections and how diagnostics_data_path/limit/offset interoperate for paging a list-valued sub-tree. This is meaningfully more than the schema's per-parameter prose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Get Home Assistant system health') and immediately scopes it to the differentiator (ZHA, Z-Wave JS, per-integration diagnostics). An agent can distinguish it from siblings like ha_get_logs, ha_get_integration, or ha_get_overview based on the description alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides two concrete invocation examples, including the diagnostics pagination loop (repeat with diagnostics_data_offset while has_more) and a combined-sections example. It implies when to use optional sections, though it does not explicitly state when to prefer this tool over siblings such as ha_get_logs for connectivity troubleshooting.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_todoGet TodoA
Read-onlyIdempotent

Get todo lists or items - list all todo lists or get items from a specific list.

Without an entity_id: lists every entity in the 'todo' domain (shopping lists and other todo-type integrations) with entity_id, friendly_name and state (number of incomplete items or current status).

With an entity_id: returns that list's items — uid, summary, status (needs_action or completed), description, and due date where supported — optionally filtered by status (None returns all).

EXAMPLES:

  • List all todo lists: ha_get_todo()

  • Get incomplete items: ha_get_todo("todo.shopping_list", status="needs_action")

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNoFilter items by status: 'needs_action' for incomplete, 'completed' for done. Only applies when entity_id is provided.
entity_idNoTodo list entity ID (e.g., 'todo.shopping_list').

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint=false, so safety is covered. The description adds useful semantics beyond that: the meaning of 'state' (number of incomplete items) and the note that status=None returns all. Return-value detail is partly redundant with the output schema, keeping this short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core split, followed by mode-specific detail and two examples. Every sentence carries information; the parenthetical about shopping lists is helpful domain color rather than filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With annotations covering safety and an output schema handling return shape, the description supplies everything else an agent needs: the two invocation modes, the filter semantics, and worked examples.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description restates the status/entity_id interaction and adds 'None returns all', but contributes little that the schema does not already document.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (get) and resource (todo lists or items) and explicitly splits the two operating modes by presence/absence of entity_id. It also names the domain ('todo') so an agent can distinguish it from sibling entity tools like ha_get_entity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly describes when each mode applies ('Without an entity_id...', 'With an entity_id...') and gives concrete examples. It does not, however, contrast itself with the mutation siblings (ha_set_todo_item, ha_remove_todo_item), so read-vs-write routing is only implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_get_zoneGet ZoneA
Read-onlyIdempotent

Get zone information - list all zones or get details for a specific one.

Without a zone_id: Lists all Home Assistant zones with their coordinates and radius. With a zone_id: Returns detailed configuration for a specific zone.

EXAMPLES:

  • List all zones: ha_get_zone()

  • Get specific zone: ha_get_zone(zone_id="abc123")

With the ha_mcp_tools custom component installed, YAML-defined zones — including the auto-synthesized 'home' zone — are included and marked editable=false / source="yaml" (storage zones created via UI/API are source="storage"). Without the component, only storage zones are listed and YAML-defined zones such as 'home' will not appear.

ParametersJSON Schema
NameRequiredDescriptionDefault
zone_idNoZone ID to get details for (from ha_get_zone() list).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/openWorld=false, so safety is covered. The description adds real behavioral context beyond that: YAML-defined zones only appear with the custom component, and results are marked editable=false/source='yaml' vs 'storage'. That is genuinely useful disclosure, though it doesn't cover pagination or return shape.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core behavior and example calls before the more niche YAML/storage caveat. The component paragraph is long but earns its place by preventing silent empty results; overall well-structured with minor verbosity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-optional-param read tool with an output schema covering return values, the description is complete: both invocation modes, examples, and the environment-dependent caveat on which zones are returned are all present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the zone_id description already says it comes from the list call. The description reinforces the two-mode semantics of zone_id but adds little syntax or format detail the schema lacks, so baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get zone information') and immediately distinguishes the two modes: list-all vs. detail-for-one. An agent can tell this apart from sibling mutations like ha_set_zone and ha_remove_zone without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly describes the condition that selects each mode, with concrete examples for both calls. It does not name the sibling alternatives (ha_set_zone/ha_remove_zone) for when a write is actually intended, so it stops short of the top score.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_list_floors_areasList Floors and AreasA
Read-onlyIdempotent

List floors sorted by level ascending, each with their assigned areas nested, plus areas without a floor.

Use for location-based reasoning where floor-to-area relationships matter, such as "which rooms are on the ground floor" or operations scoped to a level.

Floors with level=None sort alongside level 0 (ground floor). Areas without a floor assignment appear in unassigned_areas; areas whose floor_id points to a non-existent floor appear in orphaned_areas. When the ha_mcp_tools component's registries capability is available, both registries come from a single in-process snapshot, so this classification is always consistent. Without it (legacy path), the two registries are fetched via independent WebSocket calls and a registry change between reads may transiently misclassify an area.

ParametersJSON Schema
NameRequiredDescriptionDefault
fieldsNoReturn only the specified top-level response keys to reduce response size (e.g. ["floors"]). None = full response. Available keys: success, floor_count, area_count, unassigned_count, orphaned_count, floors, unassigned_areas, orphaned_areas, message.
area_fieldsNoProject each area record (in floors[].areas, unassigned_areas, and orphaned_areas) to only the specified keys. E.g. ["area_id", "name"] returns slim area records. None = full records. Unknown keys yield empty records. Available keys: area_id, name, icon, floor_id, aliases, picture, labels.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, and closed-world behavior, but the description adds substantial context beyond them: level=None sorting alongside level 0, the unassigned_areas and orphaned_areas classifications, and the in-process snapshot vs. legacy WebSocket consistency caveat with its transient misclassification risk.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core operation, followed by usage context and behavioral caveats. Every sentence is relevant, though the consistency/legacy-path paragraph is fairly dense for a simple list tool and could be slightly tighter.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema available, the description need not explain return values, and it already covers classification semantics, sorting behavior, and data-consistency caveats. Nothing an agent needs to call and interpret this tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both parameters (fields, area_fields) are fully documented in the schema with available keys and projection semantics. The description adds no parameter-specific syntax or format guidance, which is the expected baseline when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (list) and resource (floors, areas) with a precise structural description: floors sorted by level ascending with nested areas plus unassigned/orphaned areas. An agent can immediately tell this is a read-only location-hierarchy listing tool, distinct from sibling mutation tools like ha_set_area_or_floor.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says to use it for location-based reasoning where floor-to-area relationships matter and gives concrete examples ('which rooms are on the ground floor', operations scoped to a level). It does not name alternative tools or state when not to use it, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_list_servicesList Available ServicesA
Read-onlyIdempotent

List available Home Assistant services with optional pagination and detail control.

Discovers services/actions that can be called via ha_call_service.

EXAMPLES:

  • Light services with full parameter details: ha_list_services(domain="light", detail_level="full")

  • Search: ha_list_services(query="temperature")

  • Next page: ha_list_services(offset=50)

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax services to return per page
queryNoSearch in service names and descriptions.
domainNoFilter by domain (e.g., 'light', 'switch', 'climate').
fieldsNoReturn only the specified top-level response keys to reduce response size (e.g. ["services"]). None = full response. Available keys: success, domains, services, total_count, count, offset, limit, has_more, next_offset, detail_level, filters_applied.
offsetNoNumber of services to skip for pagination
detail_levelNo'summary': name, description, domain, service, and target when the service has one. 'full': additionally include parameter field schemas.summary
service_fieldsNoProject each service record to only the specified keys. E.g. ["name", "description"] returns slim service records. None = full records. Unknown keys yield empty records. Available keys: name, description, domain, service, target (when present), fields (full mode only).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare read-only, idempotent, and closed-world behavior, so the description mainly adds pagination and detail-control context. The examples concretely illustrate offset-based pagination and detail_level usage, which are useful behavioral traits beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short and front-loaded: one sentence gives the core purpose, a second links it to ha_call_service, and compact examples demonstrate key invocation patterns without wasted prose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the fully covered input schema, the existing output schema, and the read-only annotations, the description supplies all essential context: what the tool lists, how it relates to ha_call_service, and how to use pagination, search, and detail levels.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all seven parameters. The description's examples show domain, detail_level, query, and offset in use but do not add substantial semantics beyond what the schema already provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: listing available Home Assistant services. It also distinguishes this from ha_call_service by explaining that it discovers services/actions that can be called via ha_call_service.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description establishes the clear context of discovering callable services and provides concrete usage examples for filtering, searching, and pagination. It lacks explicit when-not-to-use guidance or direct comparison against other listing/search siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_manage_appManage App (add-on)A
Destructive

Manage Home Assistant apps (add-ons) or proxy an app API.

For app inventory, status, and Supervisor metadata, call ha_get_app first; use proxy mode here for documented app-specific read APIs, and do not infer private app API schemas.

Use exactly one mode: lifecycle/store action, configuration fields, path proxy, or path with array_patch.

Requires Home Assistant OS or Supervised. When ha-mcp itself runs as an app it cannot update its own running slug; update ha-mcp from the Home Assistant Apps UI. options merges top-level keys and one nested mapping level; supply complete values for deeper nested mappings because they are replaced. Prefer Ingress: direct-port access requires a shared container network and may require weakening the target app authentication. If a Supervisor lifecycle, configuration, or repository write has an unknown outcome, verify durable state with ha_get_app before retrying. That cannot prove whether restart or rebuild ran; inspect Supervisor jobs and logs and do not automatically replay them. For a proxy or array-patch write, query the target app's own read API before retrying.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

manage app apps addon add-on configure settings options port network boot watchdog auto_update supervisor ingress proxy websocket api rest esphome nodered node-red frigate mosquitto mqtt zigbee2mqtt zigbee z-wave zwave appdaemon hacs studio code server file editor terminal ssh samba grafana influxdb deconz motioneye compile validate upload deploy firmware ota flash yaml device logs flows events stats

ParametersJSON Schema
NameRequiredDescriptionDefault
bodyNoProxy mode only. Request body for POST/PUT/PATCH — or, with websocket=True, the initial WebSocket message. Pass a JSON object or JSON string.
bootNoConfig mode: Boot strategy — 'auto' (start with HA) or 'manual'.
pathNoProxy mode: API path relative to the app (add-on) root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode.
portNoProxy mode only. Connect to this port instead of the Ingress port. Use ha_get_app(slug='...') to find available ports. Some apps, including Node-RED, reject direct access unless their leave_front_door_open option is enabled and the app is restarted.
slugNoApp (add-on) slug (e.g., '<prefix>_nodered', '<prefix>_frigate'). Slug prefixes vary by app repository — call ha_get_app() to discover the actual installed slug. Required except for action='add_repository' / 'remove_repository' (which take 'repository' instead) and action='check_updates' (which takes neither).
debugNoProxy mode only. Include diagnostic info (request URL, headers sent, response headers).
limitNoProxy mode only. HTTP: return at most this many items from a JSON array response.
actionNoLifecycle mode: run a Supervisor app (add-on) action. One of 'install', 'uninstall', 'start', 'stop', 'restart', 'rebuild', 'update'. 'install'/'update' require the app's repository to be registered (it appears in ha_get_app(source='available')); 'rebuild' is for a local app whose source changed but whose version did not. Store-repository mode: 'add_repository' / 'remove_repository' register or unregister a custom app store repository — these use the 'repository' param instead of 'slug'. Store-wide mode: 'check_updates' reloads the store so Supervisor re-scans its repositories (e.g. after editing a local app's config.yaml) — it takes neither 'slug' nor 'repository', refreshes available metadata only, and installs nothing; follow with action='update' for a newer version, or action='rebuild' for a local app whose source changed without a version change. Returns 'changed' and 'updates_available', null when the store could not be read (see 'warnings').
methodNoProxy mode only. HTTP method: GET, POST, PUT, DELETE, PATCH.GET
offsetNoProxy mode only. HTTP: skip this many items in a JSON array response.
networkNoConfig mode: Complete desired host-port override map (e.g., {'5800/tcp': 8081}). A non-empty map replaces current overrides, so omitted entries are cleared. Omit 'network' to leave mappings unchanged. An empty map is ignored and does not by itself select config mode.
optionsNoConfig mode: App (add-on) configuration values (the 'Configuration' tab in the UI).
watchdogNoConfig mode: Enable or disable Supervisor watchdog (auto-restart on crash).
summarizeNoProxy mode only. WebSocket: when True, collapse runs of non-signal messages (typically YAML config dumps) into short elision markers. Set to False to return the raw stream.
websocketNoProxy mode only. Use WebSocket instead of HTTP for an app (add-on) WebSocket API. Sends 'body' as the initial message and collects responses; command names and body schemas are app/version-specific.
repositoryNoStore-repository mode only (action='add_repository' or 'remove_repository'). For add_repository: the repository URL (e.g., 'https://github.com/balloob/home-assistant-addons'). For remove_repository: the repository slug (e.g., '0f1cc410', as shown in ha_get_app(source='available')). Required for those actions; rejected otherwise.
array_patchNoArray-patch mode: atomically GET a JSON array endpoint, apply ordered ops, then POST the mutated array back. Requires 'path'; mutually exclusive with body / websocket / offset / limit and config params. Use ha_get_skill_guide for operation shapes.
auto_updateNoConfig mode: Enable or disable automatic updates for this app (add-on).
message_limitNoProxy mode only. WebSocket: cap on messages collected from the wire, bounded by an internal safety ceiling. None = collect up to the ceiling.
message_offsetNoProxy mode only. WebSocket: drop this many messages from the start of the collected list before returning.
wait_for_closeNoProxy mode only. WebSocket: True waits for the server to close a run-to-completion stream. False returns after the first response batch; use for one-shot command/response or bounded capture on a channel that stays open.
request_headersNoProxy/array-patch mode: extra HTTP headers for the app (add-on) API. Useful for app-specific requirements such as Node-RED's `Node-RED-Deployment-Type: full`. Ingress routing headers override caller values on Ingress routes; direct-port calls have no internal routing headers. `Content-Type` is derived from the body when supplied. Not valid in config or websocket mode.
python_transformNoProxy mode only. Sandboxed Python expression that post-processes the response. Variable `response` is exposed — a list[dict | str] for WebSocket (parsed JSON or raw text), or dict/list/str for HTTP (parsed body). Supports in-place mutation (response.append(...)) or reassignment (response = [...]). Example: response = [m for m in response if 'ERROR' in str(m)].

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructive/openWorld/non-idempotent, and the description goes well beyond them: OS/Supervised requirement, the inability to self-update ha-mcp's own running slug, the merge-vs-replace semantics of 'options' and 'network', the preference for Ingress over direct-port access, and explicit retry guidance for unknown-outcome writes (verify with ha_get_app, inspect Supervisor jobs, do not replay restart/rebuild). This is unusually rich behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and mode selection are front-loaded, followed by requirements and retry caveats, which is good structure. The trailing keyword blob (esphome, nodered, frigate, ...) is long and token-heavy, slightly diluting the concision of an otherwise disciplined body.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 23-parameter, multi-mode, destructive tool with an output schema and full annotation coverage, the description supplies everything an agent needs: mode disambiguation, environmental prerequisites, Ingress vs direct-port tradeoffs, and failure-recovery rules. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds cross-parameter semantics the schema lacks: which param selects which mode, that options merges only one nested level (deeper levels are replaced), and that a non-empty network map clears omitted entries. It stops short of covering every parameter, so not a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Manage Home Assistant apps (add-ons) or proxy an app API') and explicitly distinguishes itself from the sibling ha_get_app, telling the agent to use that tool first for inventory/status/metadata. An agent can tell which tool to pick without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly enumerates the four mutually exclusive modes ('lifecycle/store action, configuration fields, path proxy, or path with array_patch') and says 'use exactly one mode'. It also names the when-not case ('do not infer private app API schemas') and routes to ha_get_app and ha_get_skill_guide for prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_manage_backupManage BackupsA
Destructive

Manage Home Assistant backups — both full HA snapshots AND per-edit auto-backups.

Pick the scope first, then the action. Wrong scope routes through the wrong code path:

scope

action

What it does

snapshot

create

Create a full HA tarball (config + addons, no DB by default). Can take a while on a large instance; progress heartbeats are sent while waiting.

snapshot

list

List full HA tarball snapshots (id, name, date, size). Read-only — use to discover a backup_id or confirm a backup landed.

snapshot

restore

Restore a full HA tarball. Restarts HA. Last-resort recovery.

snapshot

delete

Delete one full HA tarball by backup_id (confirm=True required). Disabled by default (enable_snapshot_delete setting) and layered with guards even when enabled — see below.

edits

create

On-demand snapshot of one entity (domain + entity_id required). Use before the user manually edits in the HA UI. Same handler path the decorator takes on writes; bypasses the enable_auto_backup toggle.

edits

list

List per-entity auto-backups (lightweight). Filter by domain and/or entity_id.

edits

view

Read one auto-backup file by name; returns YAML and parsed config.

edits

diff

Compare one auto-backup against the entity's current config. RFC 6902 JSON-Patch + add/remove/replace counts; bounded output. Read-only — fetches the live config, makes no changes.

edits

restore

Re-apply one auto-backup. Existing Template helpers require a fresh safety snapshot; other domains follow auto-backup settings and may proceed without one. A deleted Template helper is recreated with a new config-entry ID; its saved entity ID is restored if unoccupied. No HA restart.

edits

delete

Delete one auto-backup by backup_name, or bulk-delete by filter.

When to use which scope:

  • Use scope="edits" to undo a recent automation/script/scene/dashboard/helper edit by the agent. Lightweight, fast, no restart.

  • Use scope="snapshot" only for system-wide recovery (botched add-on update, mass config corruption, etc.).

scope="snapshot" backup-hint: Run before operations that CANNOT be undone (e.g., deleting devices). If the current definition was fetched or can be fetched, this tool is usually not needed.

(snapshot, delete) is off by default and layered even when enabled: a human must set enable_snapshot_delete=true (env var, web settings UI, or add-on Supervisor options) — an agent cannot turn this on itself. When enabled, a delete call is still refused if: the target is a scheduled/automatic backup; it's younger than snapshot_delete_min_age_days (default 7, 0 disables the floor); or it's the single newest snapshot remaining. These guarantee at least one recovery point always survives an agent's own mistakes.

enable_auto_backup and scope="edits": the automatic-on-write capture (every wrapped tool call) is gated by enable_auto_backup=true — if the listing is empty, check the toggle (web settings UI or ENABLE_AUTO_BACKUP=true env var). The explicit (edits, create) action bypasses the toggle since the request is explicit; list / view / restore / delete operate on whatever's already on disk regardless of the toggle's current state.

Template filters: edits.create accepts a Template entity ID and returns its stable config-entry ID as entity_id. Use that returned ID for edits.list and bulk edits.delete; those filters do not resolve entity aliases. After recreation, the restore result reports the replacement config-entry ID and entity_id_mapping separately.

Examples:

  • Snapshot before risky op: ha_manage_backup(scope="snapshot", action="create", name="Before_Big_Change")

  • List snapshots (to discover a backup_id or confirm one landed): ha_manage_backup(scope="snapshot", action="list")

  • Restore full snapshot: ha_manage_backup(scope="snapshot", action="restore", backup_id="dd7550ed")

  • Delete an old snapshot (requires enable_snapshot_delete=true): ha_manage_backup(scope="snapshot", action="delete", backup_id="dd7550ed", confirm=True)

  • On-demand entity snapshot before a manual UI edit: ha_manage_backup(scope="edits", action="create", domain="helper_input_boolean", entity_id="kitchen_lights_active")

  • List recent auto-backups for one automation: ha_manage_backup(scope="edits", action="list", domain="automation", entity_id="kitchen_lights")

  • View an auto-backup: ha_manage_backup(scope="edits", action="view", backup_name="automation.kitchen_lights.20260521_153000.yaml")

  • Diff an auto-backup vs current state: ha_manage_backup(scope="edits", action="diff", backup_name="automation.kitchen_lights.20260521_153000.yaml")

  • Restore an auto-backup: ha_manage_backup(scope="edits", action="restore", backup_name="automation.kitchen_lights.20260521_153000.yaml")

  • Delete one auto-backup: ha_manage_backup(scope="edits", action="delete", backup_name="...")

  • Bulk-delete old auto-backups: ha_manage_backup(scope="edits", action="delete", older_than_days=30)

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNo(snapshot.create) Tarball name. Auto-generated if not provided.
limitNo(edits.list / snapshot.list) Maximum number of entries to return.
scopeYes'snapshot' for full HA tarballs; 'edits' for per-entity auto-backups.
actionYesOperation to perform. Valid (scope, action) combinations are listed in the tool description.
domainNo(edits.create / edits.list / edits.delete) Filter auto-backups by domain (e.g. 'automation', 'helper_timer'). Required for edits.create.
confirmNo(snapshot.delete) Must be True to confirm deletion.
backup_idNo(snapshot.restore / snapshot.delete) Tarball ID (e.g. 'dd7550ed').
entity_idNo(edits.create / edits.list / edits.delete) Filter auto-backups by entity ID. Required for edits.create.
backup_nameNo(edits.view / edits.restore / edits.delete) Auto-backup filename (format '<domain>.<entity_id>.<timestamp>[_NN].yaml'). Not a tarball ID.
older_than_daysNo(edits.delete) Bulk-delete auto-backups older than this many days.
restore_databaseNo(snapshot.restore) Include the database in the restore; otherwise config only.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare destructiveHint=true and openWorldHint=false; the description adds substantially more: snapshot.restore restarts HA, snapshot.delete is disabled by default and layered with three refusal guards an agent cannot override, edits.create bypasses the enable_auto_backup toggle while list/view/restore/delete do not, Template recreation yields a new config-entry ID, and the tool may take a while on large instances (with a 4-minute timeout note). This is rich behavioral context well beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the 'Pick the scope first' framing and a compact action table, which is efficient. However, the Examples section largely restates rows already covered by the table, and the final Claude Desktop paragraph is somewhat tangential, so it is slightly longer than strictly necessary for a tool this complex — but still earns most of its length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 11 params, 6 actions across 2 scopes, a mutation-capable tool with an output schema present (so return values need not be explained), and annotations covering only safety hints, the description supplies everything an agent needs: scope routing, guards, toggle interactions, ID formats, and timeout guidance. Complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description meaningfully adds: the full (scope, action) validity matrix mapping params to actions, the backup_name vs backup_id distinction, Template entity-ID aliasing quirks, and worked examples showing required fields per call. This goes well past what the schema strings convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb (manage) and resource (Home Assistant backups) and immediately splits the domain into two clearly distinguished scopes (snapshot tarballs vs per-entity auto-backups) with a per-action table. An agent can tell exactly what each (scope, action) pair does and route accordingly, distinguishing it from siblings like ha_config_set_automation or ha_restart.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use guidance: 'Use scope="edits" to undo a recent edit... Use scope="snapshot" only for system-wide recovery', plus the backup-hint for irreversible operations and the exclusions ('if the current definition was fetched... usually not needed'). It also names alternatives implicitly by scoping decisions. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_manage_blueprintsManage BlueprintsA
Destructive

Manage Home Assistant blueprints — list, read, import, save, delete, or render a standalone config.

One interface for the whole blueprint lifecycle in the automation and script domains.

DO NOT use this to create an automation or script FROM a blueprint — that is ha_config_set_automation / ha_config_set_script with a use_blueprint config.

To duplicate a blueprint, get it and save its yaml under a new path; to edit one in place, get it, change the text, and save it back to the same path with overwrite=True.

get also reports used_by: the automations or scripts built on the blueprint, which is the UI's "Show automations using this blueprint". Check it before deleting — Home Assistant refuses to delete a blueprint anything still uses (the error lists the consumers), and it goes on counting a consumer that has since taken control of its own config until that consumer is removed.

CAVEATS: get returns the on-disk YAML only when something can read it — an in-process server, the ha_mcp_tools component, the File & YAML Tools entry, or the blueprint's source_url; yaml_source names which one answered, and source_url text is a fresh download that can differ from the installed file. Core's blueprint API alone exposes metadata only, so a locally authored blueprint on a bare install has no readable text. Both writes are snapshotted first when a copy can be read, so ha_manage_backup(scope="edits") can restore the previous file. substitute only renders — it writes nothing, so pass the returned config to ha_config_set_automation / ha_config_set_script to persist it. To convert an automation or script that ALREADY exists, prefer those tools' take_control_of_blueprint=True: it renders with that item's own current inputs and saves the result over itself in one call.

EXAMPLES:

  • Get one (with its consumers in used_by): ha_manage_blueprints(action="get", path="homeassistant/motion_light.yaml")

  • Import: ha_manage_blueprints(action="import", url="https://example.com/bp.yaml")

  • Edit in place: ha_manage_blueprints(action="save", path="user/motion.yaml", yaml=, overwrite=True)

  • Delete: ha_manage_blueprints(action="delete", path="user/motion.yaml", confirm=True)

  • Render standalone: ha_manage_blueprints(action="substitute", path="user/motion.yaml", input={"motion_sensor": "binary_sensor.hall"})

RELATED TOOLS: ha_config_remove_automation / ha_config_remove_script to clear consumers blocking a delete, ha_search to find them.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

blueprint blueprints import delete remove unused substitute take-control list ha_get_blueprint ha_import_blueprint

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoURL to import from — GitHub, Home Assistant Community, or a direct YAML link (action='import')
pathNoInstalled blueprint path, e.g. 'homeassistant/motion_light.yaml' (action='get' / 'save' / 'delete' / 'substitute'). 'save' appends '.yaml' when it is missing, as Home Assistant does.
yamlNoBlueprint YAML text to write (action='save')
inputNoBlueprint input values keyed by input name (action='substitute'); defaults to {}
actionYes'list' installed blueprints, 'get' one blueprint's metadata/inputs/YAML, 'import' one from a URL, 'save' YAML text to a blueprint path, 'delete' an installed one, or 'substitute' to render a standalone config (the UI's "Take control")
domainNoBlueprint domain: 'automation' or 'script'. Ignored by action='import' — the blueprint file declares its own domain.automation
confirmNoRequired confirmation for action='delete'
overwriteNoWrite over an already-installed blueprint (action='import' / 'save'). Home Assistant reloads every automation/script using it.
source_urlNoOrigin URL to stamp into the saved blueprint's metadata (action='save'); omit for a hand-authored blueprint

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Far exceeds annotation coverage: describes read caveats (yaml only readable under certain conditions, yaml_source identifies the reader), confirms source_url is a fresh download that can differ from installed file, notes both writes are snapshotted so backups can restore, and that 'substitute' only renders and writes nothing. Mentions the delete safety check based on used_by. Annotations (destructiveHint=true, openWorldHint=true, idempotentHint=false) are consistent with the described behaviors.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose and a clear DO NOT line, then workflows, caveats, and examples. Slightly long due to edge-case caveats and a user-support troubleshooting paragraph, but each section is structured and scannable. Upper-middle for size.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers the complete lifecycle for a 6-action multi-purpose tool: read limitations, write persistence, backup interaction, and how to proceed when deletes are blocked by consumers. Output schema exists, so return values don't need to be in the description; the description focuses on underlying behaviors. Adequate for the complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%; the description adds contextual meaning beyond schema: explains that 'substitute' doesn't persist and must be passed to config set tools, clarifies overwrite triggers a reload of every consuming automation/script, and documents used_by output semantics for get/delete. Adds concrete examples of each parameter usage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb set and resource: 'Manage Home Assistant blueprints — list, read, import, save, delete, or render a standalone config.' Explicitly scopes to the automation and script domains. Clearly distinguishes from the related-but-different automation/script tools by name (ha_config_set_automation / ha_config_set_script).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use and when-not-to-use guidance: 'DO NOT use this to create an automation or script FROM a blueprint — that is ha_config_set_automation / ha_config_set_script with a use_blueprint config.' Also provides step-by-step workflows for duplicate/edit and tells the user to prefer take_control_of_blueprint for existing items.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_manage_energy_prefsManage Energy Dashboard PreferencesA
Destructive

Manage the Home Assistant Energy Dashboard preferences: grid / solar / battery / gas / water energy sources, device consumption sensors for electricity and water, and cost tariffs.

WHEN TO USE:

  • mode='get' / 'set': inspect or replace the full Energy Dashboard config. Use 'set' for bulk edits or anything touching multiple top-level keys at once.

  • mode='add_device' / 'remove_device': add or remove a single device-consumption entry. The tool performs a fresh read-modify-write internally; the caller does NOT manage config_hash.

  • mode='add_source': append a single entry to energy_sources. Same atomic read-modify-write semantics.

WHEN NOT TO USE:

  • To create the underlying statistics themselves — they must already exist as HA entities before being referenced here; create them via the relevant integration's config flow first.

CAVEATS:

  • energy/save_prefs has per-key FULL-REPLACE semantics. Passing {"device_consumption": [<one entry>]} deletes every other device the user had configured — silently, with no error. mode='set' requires a fresh config_hash for optimistic locking; convenience modes hide this entirely.

  • The per-key config_hash form lets an agent submit only the top-level key it wants to change: config keys must equal the dict keys (any key outside the canonical set is rejected with VALIDATION_FAILED), and a per-key submission still fully replaces that key's value. A mismatch on any locked key returns RESOURCE_LOCKED with the offending keys in mismatched_keys.

  • dry_run=True skips the hash check entirely for both forms.

  • Writes need an administrator token; Home Assistant rejects the save otherwise.

  • After a successful write, the tool calls energy/validate and returns residual issues (missing stats, unit mismatches) as post_save_validation_errors; the save persists regardless — correct the config and write again if needed.

  • 'add_source' rejects duplicates by (type, stat_energy_from) for solar/battery/gas/water; grid entries are appended without a duplicate check (multiple grid variants are legitimate), so the caller de-duplicates grid sources.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

read get inspect energy dashboard preferences prefs electricity price prices pricing tariff tariffs rate rates cost costs kwh peak off-peak offpeak contract utility bill grid solar battery gas water consumption number_energy_price entity_energy_price stat_energy_from

ParametersJSON Schema
NameRequiredDescriptionDefault
modeYesOperation mode. Primitives: 'get' reads the current prefs; 'set' writes a full prefs payload.
nameNoDisplay name for mode='add_device'; ignored otherwise.
waterNoIf True, mode='add_device' / 'remove_device' targets 'device_consumption_water' instead of 'device_consumption'.
configNoFull prefs payload for mode='set'. Must contain the top-level keys you intend to replace: 'energy_sources', 'device_consumption', 'device_consumption_water'. Any omitted key is preserved. Call with mode='get' first, mutate the returned config, then pass the whole object back. Ignored by convenience modes.
sourceNoSingle energy_sources entry for mode='add_source'. Must contain 'type' (one of grid|solar|battery|gas|water) and the type-specific required fields (e.g. solar/battery/gas/water require 'stat_energy_from'). Every source type also accepts an optional 'name' (display label in the energy graphs); battery additionally accepts 'stat_soc' (state-of-charge statistic). Note: HA Core's voluptuous schema for grid sources requires the full field set (cost_adjustment_day, stat_energy_to, stat_cost, entity_energy_price, number_energy_price, entity_energy_price_export, number_energy_price_export, stat_compensation) — the local shape check is narrower, so a minimal {'type': 'grid'} passes locally but surfaces in post_save_validation_errors after writing. Pass the unused fields as None to satisfy the server. Required for mode='add_source'; ignored otherwise.
dry_runNoIf True, no write is performed. For mode='set': runs a local shape check on the proposed config AND calls the server's energy/validate against the CURRENT persisted state. For convenience modes: simulates the mutation against a fresh read and reports what would change without writing — but still raises RESOURCE_ALREADY_EXISTS (duplicate add_device, or duplicate add_source for solar/battery/gas/water), RESOURCE_NOT_FOUND (missing remove_device), or VALIDATION_FAILED (post-mutator shape error) when the proposed mutation is not applicable.
config_hashNoHash from a previous mode='get' call. REQUIRED for mode='set' unless dry_run=True. Two forms: str (full-blob lock) or dict (per-key lock, taken from the config_hash_per_key field of mode='get'). Pass the dict form as a native object, NOT a JSON-encoded string — a stringified dict is treated as a full-blob token and will report RESOURCE_LOCKED; clients that can only send strings should use the str full-blob form. Ignored by convenience modes.
included_in_statNo'Parent' statistic for mode='add_device'. Set this to a statistic that already INCLUDES this device's consumption (e.g., a whole-home or circuit-level meter that this device feeds into). The Energy Dashboard will subtract this device's reading from the parent so the parent's contribution is not double-counted. Ignored otherwise.
stat_consumptionNoStatistic entity_id for mode='add_device' / 'remove_device' (e.g. 'sensor.fridge_energy'). Required for those modes; ignored otherwise.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true and idempotentHint=false, and the description goes far beyond them: per-key FULL-REPLACE semantics that silently delete sibling entries, optimistic-locking behavior with RESOURCE_LOCKED/mismatched_keys, dry_run bypass of the hash check, administrator-token requirement, post_save_validation_errors persisting regardless, and asymmetric duplicate handling for grid sources. This is unusually thorough behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Long but strongly front-loaded and organized into Purpose / WHEN TO USE / WHEN NOT TO USE / CAVEATS sections, with each bullet carrying operational weight. The final paragraph about a Claude Desktop 4-minute timeout is tangential plumbing advice and the trailing keyword string is noise, which keeps this from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return formats need not be described, and annotations already carry the safety profile; against that backdrop the description supplies everything else an agent needs — mode routing, locking protocol, error semantics, auth requirement, and validation feedback — with no material gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100% with detailed per-parameter text, so the baseline is 3, but the description adds cross-parameter meaning the schema does not: that convenience modes hide config_hash entirely, that the dict form must be sent as a native object rather than a stringified dict, and the full-replace caveat on per-key submission. These are genuine additions beyond the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Manage the Home Assistant Energy Dashboard preferences') and enumerates exactly what the resource contains (energy sources, device consumption sensors, cost tariffs). It is clearly distinguishable from all sibling config tools, none of which touch the energy dashboard.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit WHEN TO USE section maps each mode to its scenario (bulk edits vs single entry vs append) and names the internal read-modify-write behavior so the caller knows it doesn't manage config_hash. A WHEN NOT TO USE section redirects to the relevant integration's config flow for creating statistics, which is the exact alternative an agent would need.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_manage_hacsManage HACSA
Destructive

Manage HACS (Home Assistant Community Store) — install/update, remove, add custom repositories, or refresh repository information.

This tool performs writes; to search the store or read repository details use ha_get_hacs_info. Use action="update_information" to run the HACS UI's "Update information" action — a forced re-fetch of one repository's release data from GitHub, so a pending update becomes visible to HACS and its update entity immediately.

Examples:

  • Install latest: ha_manage_hacs(action="download", repository_id="441028036")

  • Install a version: ha_manage_hacs(action="download", repository_id="piitaya/lovelace-mushroom", version="v4.0.0")

  • Remove: ha_manage_hacs(action="remove", repository_id="owner/repo")

  • Add a custom repo: ha_manage_hacs(action="add_repository", repository="owner/repo", category="lovelace")

  • Refresh release data: ha_manage_hacs(action="update_information", repository_id="owner/repo")

Caveats: Installing an integration usually needs a Home Assistant restart to activate; new Lovelace cards need a browser cache clear. add_repository requires owner/repo format plus a matching category. Removing an integration deletes its files but the loaded module persists until the next Home Assistant restart — delete its config entries first (ha_remove_helpers_integrations). HACS refreshes custom repositories on its own only about every 48 hours, so update_information is the way to surface a just-published release.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYes'download' to install/update, 'add_repository' to register a custom repo, 'remove' to uninstall a downloaded repo, or 'update_information' to refresh a repository's release data from GitHub
versionNoSpecific version to install (action='download')
categoryNoRepository category (action='add_repository')
repositoryNoGitHub repo 'owner/repo' to add (action='add_repository')
repository_idNoNumeric HACS ID or 'owner/repo' path (action='download' / 'remove' / 'update_information')

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and openWorldHint=true, and the description goes well beyond that: restart required after installing integrations, browser cache clear for Lovelace cards, deletion of integration files while the loaded module persists until restart, required owner/repo format plus matching category for add_repository, delete config entries first, and a timeout-recovery procedure. This is unusually rich operational context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose sentence, then bold-labeled Examples and Caveats sections, so it is scannable rather than a wall of text. The final Claude Desktop timeout paragraph is somewhat tangential and could be trimmed, but it is clearly separated and arguably useful for a long-running install.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a multi-action mutation tool with a rich schema and an output schema present (so return values need no explanation), the description covers action selection, sequencing caveats, failure recovery, and lifecycle side effects. Nothing an agent needs in order to call it correctly appears to be missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3; the description exceeds it by showing real call shapes with concrete values (repository_id='441028036' or 'piitaya/lovelace-mushroom', version='v4.0.0', category='lovelace', repository='owner/repo'), which clarifies the numeric-vs-path duality of repository_id and the version string format.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb set and resource ('Manage HACS ... install/update, remove, add custom repositories, or refresh repository information') and explicitly names the sibling it is not (ha_get_hacs_info for reads/search). An agent can separate it from that sibling without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use/when-not: 'This tool performs writes; to search the store or read repository details use ha_get_hacs_info', plus a concrete directive for action="update_information" (forced re-fetch to surface a just-published release, versus HACS's ~48h auto-refresh). Alternatives and selection conditions are stated, not inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_manage_pipelineManage Assist PipelineA
Destructive

Manage Home Assistant Assist pipelines.

Use action='list' to discover pipeline IDs, action='get' to inspect one pipeline, action='create' or action='update' to write pipeline settings, action='set_preferred' to choose the preferred pipeline, and action='process' to run a sentence through Assist.

action='process' sends the sentence straight to Assist's conversation agent, so a matched intent executes: it turns on the light rather than reporting that it would. Its result carries response_type ('action_done', 'query_answer' or 'error') and, on an error, error_code such as 'no_intent_match' — Assist declining a sentence is an answer, not a tool failure, so inspect those fields rather than expecting a raised error. Use ha_call_service to act on an entity directly; use this to test what Assist itself understands. When the built-in agent answers, a matching conversation trigger runs its automation: that agent checks its sentence triggers before it matches intents, so this is not limited to intents. Even with pipeline_id, the sentence goes to the agent directly. So with an agent other than the built-in one, neither sentence triggers nor prefer_local_intents apply — a full pipeline run is what adds those for other agents.

EXAMPLES:

  • Get the preferred pipeline: ha_manage_pipeline(action="get", pipeline_id="preferred")

  • Create by cloning a pipeline: ha_manage_pipeline(action="create", base_pipeline_id="preferred", name="Local Assist", conversation_engine="conversation.local_llm")

  • Update the agent and clear the TTS voice: ha_manage_pipeline(action="update", pipeline_id="preferred", conversation_engine="conversation.local_llm", tts_voice="")

  • Run a sentence through one pipeline's agent: ha_manage_pipeline(action="process", sentence="turn on the kitchen light", pipeline_id="preferred")

  • Continue a conversation: ha_manage_pipeline(action="process", sentence="and the hallway?", conversation_id="")

Non-nullable fields such as name, language, conversation_language, and conversation_engine must be omitted or non-empty.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoPipeline display name. Required when action='create'.
actionYesPipeline operation: list, get, create, update, set_preferred, or process.
agent_idNoFor process only, the conversation agent entity ID to answer, e.g. 'conversation.home_assistant'. Overrides the agent taken from pipeline_id; omit both for the default agent.
languageNoPipeline language, e.g. 'en'. For process, the language to recognise the sentence in.
sentenceNoNatural-language command to run through Assist. Required when action='process'.
tts_voiceNoText-to-speech voice. Pass empty string to clear.
stt_engineNoSpeech-to-text engine. Pass empty string to clear.
tts_engineNoText-to-speech engine. Pass empty string to clear.
pipeline_idNoAssist pipeline ID. Required for get, update, and set_preferred. Optional for process, where it selects the conversation agent and language that pipeline is configured with.
stt_languageNoSpeech-to-text language. Pass empty string to clear.
tts_languageNoText-to-speech language. Pass empty string to clear.
wake_word_idNoWake-word ID. Pass empty string to clear.
make_preferredNoFor create/update only, also set the resulting pipeline as preferred. Ignored for other actions.
conversation_idNoFor process only, the conversation to continue. Returned in the response so follow-up sentences keep their context.
base_pipeline_idNoPipeline ID to clone when creating. Omit to clone the preferred pipeline. Ignored for non-create actions.
wake_word_entityNoWake-word entity ID. Pass empty string to clear.
conversation_engineNoConversation agent entity ID or engine ID. Required when action='create'.
prefer_local_intentsNoWhether Home Assistant local intents should be preferred before the conversation engine.
conversation_languageNoConversation language, usually '*'.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite annotations already flagging destructive/open-world/non-idempotent, the description adds substantial behavioral context the annotations cannot: action='process' actually executes a matched intent, results carry response_type/error_code, a declined sentence is an answer not a failure, and it gives Claude Desktop timeout recovery advice. This is well beyond the annotation baseline.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The action list is front-loaded and the specialist 'process' caveats follow in a dedicated paragraph, with worked examples last. It is long, but nearly every sentence carries actionable information; a few clauses about agent/trigger interplay are dense and slightly repetitive.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 19-parameter, 6-action tool with an output schema, this is complete: action semantics, side effects, error handling, cloning/clearing conventions, and timeout recovery are all covered. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so the baseline is 3, but the description adds meaningful semantics: non-nullable fields must be omitted or non-empty, empty strings clear fields, and the worked examples map parameters to specific actions. It slightly exceeds the schema rather than merely restating it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Manage Home Assistant Assist pipelines') and then enumerates each action ('list', 'get', 'create'/'update', 'set_preferred', 'process') with its own function. An agent can tell exactly what this tool covers and how it differs from ha_call_service, which is named.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent: 'Use ha_call_service to act on an entity directly; use this to test what Assist itself understands.' It also states when process behavior differs (agent vs full pipeline run), and disambiguates trigger/intent handling. This is when-to-use and when-not guidance, not inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_manage_radioManage Radios (Z-Wave / Zigbee / Matter / Thread)A
Destructive

Manage Home Assistant radios — Z-Wave, Zigbee, Matter, and Thread.

For read-only inspection prefer ha_get_device / ha_get_system_health, which mirror the 'diagnostics' and 'network_status' actions; use this tool for writes and the active 'ping' probe (unique to this tool). Write actions perform inclusion/commissioning, removal, healing, reconfiguration, firmware updates and credential provisioning.

Caveats: long-running actions (inclusion, rebuild routes, firmware) start the operation and return immediately with long_running=true; completion happens out-of-band. Interactive Z-Wave S2 secure inclusion (read-the- PIN pairing) is not scriptable — use SmartStart/QR provisioning here or the HA UI.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
radioYesWhich radio to manage.
actionYesOperation to perform. Actions vary per radio; an unknown action returns the supported list for that radio. Common: 'diagnostics', 'network_status', 'ping', 'add'/'commission', 'remove_device', 'reinterview'/'reconfigure', 'firmware_update'.
paramsNoAction-specific parameters (e.g. code, pin, channel, property, value).
confirmNoRequired (True) to run destructive actions (e.g. remove_device, network restore, change_channel, hard_reset, remove_fabric).
device_idNoTarget device (node) for node-scoped actions.
entity_idNoResolve the device from this entity for node-scoped actions.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare destructiveHint=true, openWorldHint=false, and idempotentHint=false, but the description adds substantial behavioral context beyond them: long-running actions return immediately with long_running=true, completion is out-of-band, S2 secure inclusion is not scriptable, and it gives timeout guidance for Claude Desktop users.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the tool's purpose and then organized into usage, caveats, and environment-specific timeout guidance. It is somewhat longer than typical, but every paragraph carries actionable information for correct invocation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema exists, the description need not explain return values, and it covers the important operational context: long-running behavior, destructive-action confirmation via schema, non-scriptable Z-Wave S2 flows, and read-only alternatives. An agent has enough information to call this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all six parameters thoroughly. The description adds little new parameter-level meaning beyond naming action categories like diagnostics, ping, add/commission, and firmware_update, so the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (manage) and resource (Home Assistant radios) and names the exact radio protocols covered. It also distinguishes itself from read-only sibling tools ha_get_device and ha_get_system_health, so an agent can select it without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says to prefer ha_get_device / ha_get_system_health for read-only inspection and to use this tool for writes and the unique active 'ping' probe. It also names the write action categories and caveats for long-running and non-scriptable actions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_manage_themeManage Frontend ThemesA
DestructiveIdempotent

Manage Home Assistant frontend themes.

When NOT to use: themes are YAML files - Home Assistant has no API to create or edit them. Installing community themes goes through HACS (ha_manage_hacs); editing custom theme files goes through ha_config_set_yaml (beta, edits themes/.yaml keyed by theme name and attempts an automatic theme reload).

When to use: action='list' discovers installed theme names and the current defaults; action='set' selects the backend default theme (optionally per light/dark mode).

SCREENSHOT-ENGINE ACTIONS (per-user, not the backend default): Taking a dashboard screenshot makes the Puppet engine write the saved theme of the Home Assistant user its token belongs to, which also flips that user's live web and mobile sessions. The screenshot tools are read-only and only report this; use action='set_engine_theme' with the value quoted in their warning to put it back, and action='get_engine_theme' to inspect it. These act on that engine account's per-user profile via frontend/set_user_data, which is a different layer from the backend default that action='set' changes. Giving the engine its own dedicated user and token avoids the issue entirely.

Caveats: action='set' changes the backend-selected default only - users who explicitly picked a theme in their profile keep their choice. Theme names are validated by Home Assistant at call time.

EXAMPLES:

  • List themes: ha_manage_theme(action="list")

  • Set default theme: ha_manage_theme(action="set", theme_name="nord")

  • Set dark-mode theme: ha_manage_theme( action="set", theme_name="nord", mode="dark")

  • Restore built-in default: ha_manage_theme( action="set", theme_name="default")

  • Inspect the engine account's theme: ha_manage_theme( action="get_engine_theme")

  • Undo a screenshot's theme change (pass BOTH values from the warning, so a theme changed since then is not overwritten): ha_manage_theme(action="set_engine_theme", value={"theme": "", "dark": False}, expected_current={"theme": "default", "dark": True})

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNoWhich mode the theme applies to when action='set'. Defaults to light.
forceNoaction='set_engine_theme' only: skip the expected_current guard and overwrite unconditionally.
valueNoFrontend user-data theme object when action='set_engine_theme', e.g. {'theme': '', 'dark': False}. An empty dict restores default/auto behavior.
actionYesTheme operation: 'list' installed themes, 'set' the backend default theme, or read/restore the screenshot engine account's own per-user theme with 'get_engine_theme' / 'set_engine_theme'.
theme_nameNoTheme name when action='set'. Must be an installed theme; 'default' restores the built-in theme, 'none' resets the chosen mode to the built-in default.
expected_currentNoGuard for action='set_engine_theme': the stored theme is read immediately before the write and the write is skipped if it no longer equals this. Omitting this value or passing null both mean 'expect no stored theme', enforced like any other value; the guard is always applied unless force is set. Best-effort, not atomic: a change landing between that read and the write is not caught.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations give the safety profile (destructiveHint true, idempotentHint true, readOnly false), and the description adds substantial context beyond them: the screenshot-engine side effect that flips live per-user sessions, the read-back-before-repeat guidance, the non-atomic nature of the guard, and the caveat that 'set' touches only the backend default. This is rich disclosure the annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the negative guidance and positive guidance before examples, and every section maps to a real decision an agent must make. It is longer than typical, with a screenshot-engine digression and a timeout troubleshooting note, but these earn their place for a multi-mode tool with unusual side effects.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need no explanation, and the description still covers prerequisite paths, side effects, guard semantics, and recovery workflows for every action. Nothing needed to call this four-action tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds workflow-level meaning: what expected_current does in practice ('pass BOTH values from the warning so a theme changed since then is not overwritten'), how value restores default behavior, and the quoted-value restoration pattern. This goes beyond restating the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource (manage Home Assistant frontend themes) and enumerates the four distinct actions (list, set, get_engine_theme, set_engine_theme) with their different scopes. It explicitly distinguishes itself from siblings ha_manage_hacs and ha_config_set_yaml, so an agent can route without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Includes an explicit 'When NOT to use' block naming the correct alternatives (HACS for installs, ha_config_set_yaml for edits) and a 'When to use' block tying each action to a purpose. This is exactly the when/when-not/alternatives guidance that earns a top score.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_manage_updatesManage UpdatesA
Destructive

Manage Home Assistant updates (list, details, install, skip) and Repairs issues (ignore).

Covers Core, OS, supervisor, apps (add-ons), device firmware, and HACS update entities, plus the Repairs issues in Settings > System > Repairs. Repairs are listed by ha_get_overview (not here); pass their domain and issue_id to 'ignore_repair' / 'unignore_repair'. In Read Only Mode the read actions ('list', 'get') stay available; write actions are blocked.

Installs run asynchronously in Home Assistant and can take minutes: 'install' returns once the service calls are accepted, with per-entity results. Poll action='list' to watch in_progress until installed_version reaches latest_version. action='list' also returns ha_mcp_update — this MCP server's own update status {current, latest, update_available}, so a newer ha-mcp release can be flagged.

EXAMPLES:

  • Pre-update analysis: ha_manage_updates(action="get", entity_ids=["update.home_assistant_core_update"], include_release_notes=True)

  • Update everything pending in a category: ha_manage_updates(action="install", categories=["addons", "hacs"])

  • Dismiss a repair: ha_manage_updates(action="ignore_repair", repairs=[{"domain": "sun", "issue_id": "abc"}])

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

update updates install skip firmware core os repair repairs issue ignore dismiss unignore

ParametersJSON Schema
NameRequiredDescriptionDefault
actionNo'list' (all pending updates, default), 'get' (details/release notes for one update), 'install' (apply pending updates), 'skip' (hide the offered version), 'clear_skipped' (re-offer a skipped version), 'ignore_repair' (dismiss Repairs issues, as the Repairs UI's Ignore does), or 'unignore_repair' (show them again).list
backupNoFor install: create a backup before installing where the update entity supports it (apps (add-ons)).
repairsNoFor ignore_repair / unignore_repair: the Repairs issues to act on, as [{'domain': ..., 'issue_id': ...}] taken from ha_get_overview's repairs.
categoriesNoFor install: apply every pending update in these categories ('addons', 'hacs', 'devices', 'other'). As with the HA 'Update all' button, core/os/supervisor are excluded (target those individually via entity_ids) and skipped updates are never included.
entity_idsNoUpdate entity_id(s) to act on. 'get' takes exactly one; skip/clear_skipped require at least one; for install, mutually exclusive with categories.
include_skippedNoFor list: include updates that have been skipped.
include_release_notesNoFor get on a Core update entity: fetch multi-version release notes and breaking changes for all versions between installed and latest. Adds breaking_changes, multi_version_release_notes, and installed_integrations to the response.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (destructiveHint/openWorldHint), the description discloses that installs are asynchronous and return before completion, that polling action='list' tracks in_progress until installed_version matches latest_version, the Read Only Mode split between read and write actions, and the extra ha_mcp_update payload. It even documents the 4-minute timeout symptom and remediation, which annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action list and organized into scope, workflow, and examples sections, so structure is strong. It is somewhat long and closes with a raw keyword dump ('update updates install skip firmware core os repair repairs issue ignore dismiss unignore') that does no real work, keeping it out of the top tier.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter mutation-capable tool with an output schema and annotations already present, the description covers scope, read/write gating, async return semantics, polling, and troubleshooting. Nothing material an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so every parameter is already documented in the schema with the same action semantics, category exclusions, and mutual-exclusion rules. The description names actions and params but adds little beyond what structured data already provides, meeting the baseline 3 for high coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (Home Assistant updates/Repairs) and the concrete operations (list, get, install, skip, clear_skipped, ignore_repair, unignore_repair), and explicitly scopes the covered entity types (Core, OS, supervisor, apps, firmware, HACS). It also differentiates from siblings by noting ha_get_overview lists Repairs, not this tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use guidance, names the alternative for Repairs listing (ha_get_overview), explains Read Only Mode behavior, and routes the agent to entity_ids vs categories for installs. The EXAMPLES block further anchors typical invocations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_reload_coreReload Core ComponentsA
Destructive

Execute a Home Assistant configuration reload without a full restart.

Reloads specific configuration components so changes take effect without restarting Home Assistant — much faster than a full restart. Use it after editing automation/script YAML, adding YAML helpers, or changing customize.yaml or themes.

EXAMPLES:

  • ha_reload_core(target="automations")

  • ha_reload_core(entry_id="abc123")

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
targetNoWhat to reload: 'all', 'automations', 'scripts', 'scenes', 'groups', 'input_booleans', 'input_numbers', 'input_texts', 'input_selects', 'input_datetimes', 'input_buttons', 'timers', 'templates', 'persons', 'zones', 'core' (customize, packages) or 'themes'.all
entry_idNoReload a SINGLE config entry (one integration instance) instead of sweeping subsystems — the fast path after editing a custom component on disk. Pass it alone (leave target at 'all'); combining it with an explicit target is a validation error. Find the id via ha_get_integration.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and openWorldHint=false, so the safety profile is covered. The description adds valuable behavioral context about the 4-minute timeout, reading the target back before repeating, and the manual-approve workflow, though it does not elaborate on what makes a reload destructive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads purpose, then usage, then examples, and closes with a practical timeout note. The final sentence is long but contains actionable guidance that earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and annotations covering safety, the description supplies purpose, use cases, examples, and a timeout edge case. It could name ha_restart explicitly as the heavier alternative, but is otherwise complete for a reload tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents both parameters, including the target enum and the entry_id validation rule. The description's examples add syntax but no additional semantic detail beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Execute reload), resource (Home Assistant configuration), and scope (without full restart), and distinguishes itself from a full restart, which maps to the sibling ha_restart. An agent can tell it reloads components rather than restarting.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says to use it after editing automation/script YAML, adding YAML helpers, or changing customize.yaml or themes, and examples show both target and entry_id paths. It contrasts with a full restart but does not name ha_restart as the alternative for heavier operations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_remove_area_or_floorRemove Area or FloorA
DestructiveIdempotent

Remove a Home Assistant area or floor.

Removing an area unassigns its entities and devices (the entities and devices themselves are not removed). Removing a floor unassigns its areas. May break automations referencing the removed area/floor.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesArea ID or floor ID to delete (use ha_list_floors_areas to find IDs)
kindYesWhich registry to delete from: 'area' or 'floor'

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, so safety framing is partially covered. The description adds valuable specific context: what gets unassigned (entities/devices for areas, areas for floors), that entities/devices themselves survive, and that automations may break. The Claude Desktop timeout/approval paragraph is operational troubleshooting rather than behavioral disclosure of the tool itself, so it does not lift this beyond a 3.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The first paragraph is well front-loaded and efficient. The second paragraph about 4-minute timeouts and manual-approve buttons is much longer than the functional description and is largely platform-specific troubleshooting rather than tool semantics, which dilutes focus.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained. The description covers effect, side effects, and risk for a destructive tool, plus the ID lookup hint. It omits auth/permission requirements and reversibility, but is largely complete for a 2-parameter destructive tool with full schema coverage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both id and kind are already documented in the schema, including the enum values and the pointer to ha_list_floors_areas. The description adds no parameter syntax or format details beyond the schema, so baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Remove) and resource (Home Assistant area or floor), and the two target kinds are explicit. It is distinguishable from sibling ha_set_area_or_floor and ha_list_floors_areas by the destructive framing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied through the effect descriptions (when removing an area vs floor), and the schema points to ha_list_floors_areas for ID lookup. However, there is no explicit when-to-use guidance, no mention of prerequisites or alternatives like ha_set_area_or_floor, and no exclusion conditions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_remove_deviceRemove DeviceA
DestructiveIdempotent

Remove an orphaned device from the Home Assistant device registry.

WARNING: This removes the device entry from the registry.

  • Use only for orphaned devices that are no longer connected

  • Active devices will typically be re-added by their integration

  • Associated entities may also be removed

Works only when the integration supports removing devices.

EXAMPLES:

  • Remove orphaned device: ha_remove_device("abc123def456")

NOTE: For most use cases, consider disabling the device instead: ha_set_device(device_id="abc123", disabled_by="user")

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
device_idYesDevice ID to remove from the registry

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, idempotentHint=true, and openWorldHint=false, but the description adds important specifics beyond them: removal deletes the registry entry, associated entities may also be removed, active devices may reappear via integration, and a timeout/manual-approval caveat is included. These details materially improve safe invocation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose and warning are front-loaded, and the bullet structure makes the constraints easy to scan. The Claude Desktop timeout note is somewhat lengthy and niche, but it is operational guidance for a destructive tool and does not bury the core instruction.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the destructive nature, available annotations, one fully described parameter, and an output schema that handles return values, the description covers what an agent needs: purpose, safety warnings, alternative action, capability precondition, and operational timeout behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is one parameter, device_id, and the schema coverage is 100%, with the schema itself describing it as 'Device ID to remove from the registry.' The description adds an example call with a sample ID, but no extra format, constraints, or semantics beyond the schema, so the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Remove an orphaned device from the Home Assistant device registry.' It distinguishes the operation from sibling device tools like ha_set_device and ha_get_device by scoping it to orphaned devices and warning against use on active devices.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-to-use guidance ('Use only for orphaned devices that are no longer connected') and when-not guidance ('Active devices will typically be re-added by their integration'), plus an alternative to consider ('For most use cases, consider disabling the device instead: ha_set_device(...)'). It also states a capability precondition: 'Works only when the integration supports removing devices.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_remove_entityRemove EntityA
DestructiveIdempotent

Remove one or more entities from the Home Assistant entity registry.

Permanently removes the entity registration; the entity will no longer appear in the UI or be available to automations. Use only for orphaned or stale entity entries: if the underlying device or integration is still active, the entity may be re-added automatically on the next HA restart or reload. This cannot be undone without restoring from backup. For most use cases, consider disabling instead: ha_set_entity(entity_id="sensor.old", enabled=False).

BULK MODE: Pass a list of entity IDs to remove up to 100 at once — handy for clearing the restored=true orphans an integration leaves behind after its filters change. Removals run sequentially and return: {removed: [...], skipped: [...], errors: [{entity_id, code, message}]} where skipped = ids already absent (not-found is idempotent, not an error). Bulk mode is NOT auto-backed-up (the snapshot is single-entity); single-id removal still is.

EXAMPLES:

  • Remove orphaned sensor: ha_remove_entity("sensor.old_temperature")

  • Bulk cleanup: ha_remove_entity(["sensor.orphan_1", "sensor.orphan_2"])

Use ha_search / ha_get_entity to verify an entity before removing it.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
entity_idYesEntity ID, or a list of entity IDs, to remove from the entity registry (e.g., 'sensor.old_temperature').

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, but the description adds substantial beyond-schema context: irreversibility without backup, automatic re-add semantics, that bulk mode is NOT auto-backed-up while single-id removal is, sequential execution, and that not-found is idempotent rather than an error. This is unusually informative for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded and well-sectioned (core behavior, bulk mode, examples), with no wasted filler in the main body. The closing paragraph about Claude Desktop timeout/approval behavior is operational advice rather than tool semantics and adds length that isn't strictly necessary for selection or invocation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, yet the description volunteers the bulk return shape ({removed, skipped, errors}) and the single-vs-bulk backup asymmetry, leaving no ambiguity about outcomes. Combined with the destructive and idempotent annotations, an agent has everything needed to call this safely and correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the anyOf string/array shape is documented, so baseline is 3. The description goes further by explaining the array form's operational semantics (up to 100 ids, sequential removal, per-id skipped/errors reporting) and supplies concrete example values, which meaningfully exceeds the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Remove ... entities from the Home Assistant entity registry') and immediately scopes it to registry entries, distinguishing it from device removal or service calls. The description makes clear this is permanent unregistration, not just disabling, and names the disable alternative explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use ('only for orphaned or stale entity entries'), when-not ('if the underlying device or integration is still active, the entity may be re-added'), and names the preferred alternative with a concrete call: ha_set_entity(entity_id="sensor.old", enabled=False). It also routes the agent to ha_search / ha_get_entity for pre-verification.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_remove_helpers_integrationsRemove Helper or IntegrationA
DestructiveIdempotent

Remove a Home Assistant helper or integration config entry.

Unifies three backend removal mechanisms — simple-helper websocket delete, config-entry delete, and config-subentry delete — behind one entry point with four routing paths driven by helper_type.

WHEN NOT TO USE:

  • Removing only an entity (without deleting its underlying helper or config entry) — use ha_remove_entity instead.

  • YAML-configured helpers — they have no storage backend. Edit the YAML file and reload the relevant integration.

ROUTING:

  • SIMPLE helper_type (input_button, input_boolean, input_select, input_number, input_text, input_datetime, counter, timer, schedule, zone, person, tag) + bare helper_id or entity_id → websocket delete.

  • FLOW helper_type (template, group, utility_meter, derivative, min_max, threshold, integration, statistics, trend, random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat, history_stats, mold_indicator) + full entity_id → resolve entity_id to config_entry_id via entity_registry, then delete the config entry. All sub-entities (e.g. utility_meter tariffs) are removed together.

  • helper_type=None + entry_id → direct config entry delete (any integration).

  • helper_type="config_subentry" + parent entry_id + subentry_id → delete one config subentry.

A target that is confirmed absent raises a structured error rather than returning silent success: ENTITY_NOT_FOUND for a SIMPLE target missing from both the state machine and the entity registry, or a FLOW entity_id missing from the registry (a bare helper_id on a FLOW target also raises it — FLOW resolution needs a full entity_id); RESOURCE_NOT_FOUND for a YAML-configured helper with no config entry, a config entry the backend reports as 404, or a missing config subentry. Calling N times gives the same response. Transient connectivity failures raise their own codes (WEBSOCKET_DISCONNECTED, CONNECTION_FAILED) so retry logic can branch separately.

EXAMPLES:

  • Remove SIMPLE button: ha_remove_helpers_integrations(target="my_button", helper_type="input_button", confirm=True)

  • Remove FLOW utility_meter (any sub-entity works): ha_remove_helpers_integrations(target="sensor.energy_peak", helper_type="utility_meter", confirm=True)

  • Remove any integration by entry_id: ha_remove_helpers_integrations(target="01HXYZ...", confirm=True)

  • Remove a config subentry: ha_remove_helpers_integrations(target="01HXYZ...", helper_type="config_subentry", subentry_id="subentry-123", confirm=True)

WARNING: Removing a helper or integration that is referenced by automations, scripts, or other integrations may cause those to fail. Use ha_search() / ha_get_integration() to verify before removal. Recovery requires a usable backup and supported restore path.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
waitNoWait for entity removal. Default: True. Ignored when helper_type=None or helper_type='config_subentry' (no entity poll, require_restart returned).
targetYesWhat to remove. One of: (a) bare helper_id for SIMPLE helpers, e.g. 'my_button'; (b) full entity_id, e.g. 'input_button.my_button' or 'sensor.my_meter'; (c) config entry_id for any integration, e.g. value from ha_get_integration(); (d) parent config entry_id for a config subentry.
confirmNoMust be True to confirm removal.
helper_typeNoHelper type. Required when target is a helper_id (bare) or entity_id. Set to None when target is a config entry_id to remove any integration. Use 'config_subentry' to remove a config subentry under target.
subentry_idNoConfig subentry ID to remove when helper_type='config_subentry'.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover destructiveness and idempotency, but the description adds substantial context beyond them: structured error codes (ENTITY_NOT_FOUND, RESOURCE_NOT_FOUND, WEBSOCKET_DISCONNECTED), explicit idempotency ('Calling N times gives the same response'), dependency-breakage warnings, and recovery constraints requiring a backup.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose with clean labeled sections (WHEN NOT TO USE, ROUTING, EXAMPLES, WARNING). It is long, and the EXAMPLES section largely restates the ROUTING cases plus the trailing Claude Desktop timeout note is operationally tangential, but every block is scannable and earns most of its space.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, multi-path tool with an output schema, the description covers routing, error semantics, dependency risk, and recovery in enough depth that an agent can call it correctly. Return values are left to the output schema, which is the right division of labor.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the ROUTING section adds real meaning the schema does not: it maps helper_type values to distinct backend behaviors, explains when helper_type may be None, and clarifies the required relationship between target, helper_type, and subentry_id.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb+resource ('Remove a Home Assistant helper or integration config entry') and explicitly frames the tool as unifying three removal mechanisms behind one entry point. It also differentiates itself from the sibling ha_remove_entity by naming it directly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Contains an explicit 'WHEN NOT TO USE' section naming the alternative tool (ha_remove_entity) and the YAML-configured case where the tool is not applicable. The ROUTING block tells the agent exactly which input shape selects which backend path.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_remove_todo_itemRemove Todo ItemA
DestructiveIdempotent

Remove an item from a Home Assistant todo list.

Permanently deletes an item from the specified todo list. To mark it completed instead, use ha_set_todo_item() with status="completed".

EXAMPLE: ha_remove_todo_item("todo.shopping_list", "Buy milk")

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
itemYesItem to remove - can be the item UID (from ha_get_todo) or the exact item summary/name
entity_idYesTodo list entity ID (e.g., 'todo.shopping_list')

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, so the safety profile is largely carried by structured data. The description reinforces this with 'Permanently deletes' and adds genuinely useful operational context (the Claude Desktop 4-minute timeout workaround and approval guidance) that annotations do not convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core purpose and the alternative-tool routing are front-loaded and efficient. The trailing Claude Desktop timeout paragraph is longer and more situational than the rest, slightly diluting focus, but it is not wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With annotations covering the safety profile, a complete 100%-covered schema, and an output schema present, the description need not explain return values. It covers purpose, the alternative tool, an example, and a known failure mode, leaving little needed for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so both parameters (entity_id, item) are fully documented in the schema, including that item accepts a UID or exact summary. The description only adds a positional example, so the baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the specific verb (Remove) and resource (item from a Home Assistant todo list) and clarifies the effect as a permanent deletion. This clearly distinguishes it from the sibling ha_set_todo_item, which it explicitly names.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells the agent when to use this tool versus the alternative: 'To mark it completed instead, use ha_set_todo_item() with status="completed".' The condition that selects the sibling is stated outright, and a concrete invocation example is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_remove_zoneRemove ZoneA
DestructiveIdempotent

Remove a Home Assistant zone.

EXAMPLES:

  • Remove zone: ha_remove_zone("abc123")

WARNING: Removing a zone used in automations may cause those automations to fail. Use ha_get_zone() to find the zone_id for the zone you want to remove.

NOTE: The 'home' zone cannot be removed as it is typically defined in configuration.yaml.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
zone_idYesZone ID to remove

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With annotations already declaring destructiveHint=true and idempotentHint=true, the description adds important context: automations may fail, the home zone is protected, and there is a timeout workaround. This goes well beyond what the annotations alone provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core action and then organized into examples, warnings, and notes. The Claude Desktop timeout paragraph is niche but does not obscure the main purpose; overall it is structured efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be described. The description covers the destructive nature, prerequisites, constraints, and an edge-case timeout workaround, leaving no important calling gap for an agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter is documented in the schema. The description still adds value by telling the agent to use ha_get_zone() to obtain the required zone_id, which is beyond the schema's definition.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific verb and resource: 'Remove a Home Assistant zone.' This distinguishes it from sibling tools like ha_set_zone and ha_get_zone without requiring the schema to be opened.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly names the prerequisite alternative ha_get_zone() for finding the zone_id, provides a strong warning about automations, and states the when-not case that the 'home' zone cannot be removed. This gives the agent clear routing and constraint information.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_report_issueReport Issue or FeedbackA
Read-onlyIdempotent

Get diagnostics and a finished GitHub issue for a bug report or agent feedback.

Use it when the user reports an ha-mcp error, failure or wrong result, or says you used the wrong tool or worked inefficiently. If it is unclear which, ask: "Are you reporting a bug in ha-mcp, or providing feedback on how I used the tools?"

Pass the report text in the call: the server combines it with the diagnostics it collects into issue_title, issue_body (the full report with logs) and issue_url (a new-issue link with title and body filled in, log sections left out to fit GitHub's URL limit; error messages stay in, with secrets redacted). A call without text still returns diagnostics, with placeholders in the body.

The response is LARGE; fields= narrows it. Read instructions before showing anything to the user: it covers the missing-tool and known-client pre-checks, the duplicate check, the mandatory anonymisation step, and how to file the issue. Check missing_tool_hint FIRST when the report is about a missing tool: a stale client tool list (not a bug) is the usual cause.

ParametersJSON Schema
NameRequiredDescriptionDefault
titleNoOne-line summary of what broke, for the issue title.
fieldsNoReturn only the specified top-level response keys. None = full response. Typical: 'issue_title,issue_body,issue_url,duplicate_check_urls,anonymization_guide,missing_tool_hint,known_client_issues_hint,instructions'. issue_body already embeds the relevant logs, so the raw log keys are only needed for your own analysis. Available keys: diagnostic_info, recent_logs, startup_logs, addon_logs, core_error_log, log_count, startup_log_count, formatted_report, issue_title, issue_body, issue_url, anonymization_guide, duplicate_check_urls, missing_tool_hint, known_client_issues_hint, instructions.
ai_modelNoYour own model identity, as specific as you know it. Do not invent a version.
client_appNoThe app and version the user runs you in, as the USER states it (usually on the app's About screen or from its --version command). Never guess it.
tool_callsNoThe tool call(s) that produced the problem, verbatim: name, arguments and the (shortened) response.
descriptionNoWhat went wrong in markdown: steps to reproduce, expected and actual behavior. For agent_behavior: what you did and what you should have done.
report_typeNo'runtime_bug' when ha-mcp errored or behaved wrongly; 'agent_behavior' when the user says you used the wrong tool or worked inefficiently.runtime_bug
user_promptNoThe user message that led to the problem, verbatim.
user_commentNoThe user's own words on what went wrong or what bothered them, verbatim. Ask the user for it; never write it yourself.
tool_call_countNoNumber of ha_* tool calls made since the issue started; determines how many log entries to include.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint/idempotentHint; the description goes well beyond them, disclosing that the response is LARGE, that fields= narrows it, that secrets are redacted from error messages, that log sections are dropped from issue_url to fit URL limits, that a call without text returns placeholders, and that `instructions` must be read before showing output (pre-checks, duplicate check, mandatory anonymisation). These are exactly the operational traits an agent cannot get from structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose, then usage, then return shape, then the crucial 'read instructions first' caveat — a logical order. It is dense and slightly long, but each paragraph carries distinct operational information with minimal padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 10-parameter tool with an output schema, the description covers the inputs it needs to (report text, fields, report_type) and the response keys the agent must act on (issue_title/body/url, instructions, missing_tool_hint, duplicate_check_urls), plus the anonymisation obligation. Nothing needed to invoke or consume the result correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so the baseline is 3, but the description adds real meaning: it frames the free-text report, explains the fields= narrowing and its typical value, and clarifies the runtime_bug vs agent_behavior distinction beyond the enum's own docstring. It stops short of discussing tool_call_count or ai_model, so not a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names a specific verb and resource pair ('Get diagnostics and a finished GitHub issue') and scopes it to bug reports or agent feedback. Nothing else in the sibling list (all ha_* HA control/config tools) could be confused with this diagnostic-reporting tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit triggers ('user reports an ha-mcp error, failure or wrong result', 'says you used the wrong tool'), an explicit disambiguation script when the case is ambiguous, and a routing hint ('Check missing_tool_hint FIRST when the report is about a missing tool'). This is a model of when-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_restartRestart Home AssistantA
Destructive

Execute a Home Assistant restart.

WARNING: restarts the entire Home Assistant instance; all automations are unavailable during the restart, which typically takes 1-5 minutes. Config is validated automatically before the restart proceeds (to pre-check, call ha_get_system_health(include="config_check")). For configuration changes, consider ha_reload_core() instead, which reloads specific components without a full restart.

EXAMPLE: ha_restart(confirm=True)

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
confirmNoMust be True to confirm the restart.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true and openWorldHint=false, and the description goes well beyond them: it discloses the 1-5 minute unavailability window for all automations, that config is auto-validated before proceeding, and a practical timeout-recovery procedure. This is rich behavioral context not derivable from the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The critical WARNING and duration are front-loaded, and every paragraph earns its place except the final Claude Desktop timeout note, which is client-specific guidance that is slightly verbose for a tool description. The example line is compact and useful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be described. For a destructive one-parameter operation, the description covers blast radius, duration, validation behavior, alternatives, and recovery, leaving nothing an agent needs missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single 'confirm' parameter is fully documented in the schema, so the baseline of 3 applies. The 'EXAMPLE: ha_restart(confirm=True)' line is a useful reinforcement of usage but adds no semantics beyond the schema's 'Must be True to confirm the restart.'

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Execute a Home Assistant restart') and immediately distinguishes the scope ('restarts the entire Home Assistant instance'), which separates it from sibling ha_reload_core. An agent can select it without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names the alternative (ha_reload_core) for config changes and the pre-check tool (ha_get_system_health with include="config_check"). This is when-to-use/when-not-to-use guidance with named siblings, which is exactly what the dimension asks for.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_set_area_or_floorCreate or Update Area or FloorA
Destructive

Create or update a Home Assistant area or floor.

Pass kind='area' (with optional floor_id, picture, labels) or kind='floor' (with optional level). Provide name only to create a new entry; provide id to update an existing one. Cross-kind parameters (e.g., picture or labels under kind='floor') are rejected with VALIDATION_INVALID_PARAMETER.

EXAMPLES: ha_set_area_or_floor(kind="area", name="Kitchen") ha_set_area_or_floor(kind="area", id="kitchen", floor_id="ground_floor") ha_set_area_or_floor(kind="area", id="kitchen", labels=["site_home"]) ha_set_area_or_floor(kind="floor", name="Basement", level=-1) ha_set_area_or_floor(kind="floor", id="ground_floor", level=0)

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
idNoExisting area_id or floor_id to update (use ha_list_floors_areas to find IDs)
iconNoMaterial Design Icon (e.g., 'mdi:sofa', 'mdi:home-floor-1', empty string to remove)
kindYesWhich registry to operate on: 'area' for rooms, 'floor' for building levels
nameNoName (required when creating; optional when updating, e.g., 'Living Room', 'Ground Floor')
levelNoNumeric level when kind='floor' (0=ground, 1=first, -1=basement).
labelsNoLabel IDs when kind='area' (replaces the area's label set; empty list to clear). Omit to leave labels unchanged. Floors have no labels.
aliasesNoAlternative names for voice assistant recognition (e.g., ['lounge'], empty list to clear)
pictureNoPicture URL when kind='area' (empty string to remove).
floor_idNoFloor assignment when kind='area' (use empty string to clear).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

destructiveHint=true is already declared by annotations, so the mutation risk is covered structurally. The description adds genuinely new behavior: the VALIDATION_INVALID_PARAMETER rejection for cross-kind parameters and concrete guidance for the Claude Desktop 4-minute timeout (read back before repeating). It does not restate the destroy/replace semantics in prose, but the annotation already carries that.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core operation, then the kind/name/id rule, then rejection behavior, then five compact one-line examples, then the timeout caveat. Every section earns its place, though the example block is slightly long for a nine-parameter tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema and annotations present, the description need not explain return values. It covers creation, update, validation failure, and a real timeout failure mode, leaving only minor gaps such as whether updates are reversible or whether permissions are required.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description nevertheless adds value by combining id/name into an explicit create-vs-update rule and spelling out which optional parameters are legal per kind, which is a synthesis the per-parameter schema text does not state as a rule.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb pair (create/update) and a specific resource (Home Assistant area or floor), and disambiguates the two modes via kind='area' vs kind='floor'. An agent can distinguish it from ha_list_floors_areas and ha_remove_area_or_floor without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear selection rule: 'Provide name only to create a new entry; provide id to update an existing one,' plus the cross-kind rejection rule. It stops short of explicitly naming sibling alternatives (e.g., use ha_list_floors_areas first to find IDs), so it falls just below the top band.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_set_deviceSet DeviceA
Destructive

Update device properties such as name, area, disabled state, or labels.

IMPORTANT: Renaming a device does NOT rename its entities! Device and entity names are independent. To rename entities, use ha_set_entity(new_entity_id=...).

Common workflow for full rename:

  1. ha_set_device(device_id="abc", name="Living Room Sensor") # Rename device

  2. ha_set_entity("sensor.old", new_entity_id="sensor.living_room") # Rename entities separately

EXAMPLES:

  • Rename device: ha_set_device("abc123", name="Living Room Hub")

  • Move to area: ha_set_device("abc123", area_id="living_room")

  • Disable device: ha_set_device("abc123", disabled_by="user")

  • Enable device: ha_set_device("abc123", disabled_by="")

  • Add labels: ha_set_device("abc123", labels=["important", "sensor"])

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoNew display name for the device (sets name_by_user)
labelsNoLabels to assign to the device (replaces existing labels)
area_idNoArea/room ID to assign the device to. Use empty string '' to unassign.
device_idYesDevice ID to update
disabled_byNoSet to 'user' to disable the device, or '' (empty string) to enable it. Omit to leave the disabled state unchanged.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and openWorldHint=false, so the safety profile is covered. The description adds non-obvious behavior: entity names are independent of device names, and a specific timeout-recovery procedure for Claude Desktop. It still does not explicitly state that omitted fields are left unchanged or that label assignment is destructive, though the schema covers labels replacement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose followed by the important rename caveat, then workflow and examples. The timeout paragraph is long and somewhat tangential, but it is actionable and separated from the core explanation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be described. The description covers the key trap (device vs. entity naming), a rename workflow, per-field examples, and a timeout workaround, leaving an agent well equipped to call this destructive tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description goes beyond the schema by giving concrete value examples (disabled_by="user", disabled_by="", labels=["important","sensor"]) that demonstrate intended usage patterns rather than just field meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb and resource ('Update device properties') and enumerates the mutable fields (name, area, disabled state, labels). It also explicitly differentiates itself from the sibling ha_set_entity by warning that device renames do not affect entities.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It names the alternative tool (ha_set_entity) and the exact condition that selects it, then spells out a two-step full-rename workflow with concrete calls. Per-operation examples for rename, move, disable, enable, and label make the when-to-use guidance unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_set_entitySet EntityA
DestructiveIdempotent

Update entity properties in the entity registry.

Allows modifying entity metadata such as area assignment, display name, icon, "Show As" device class override, per-domain registry options, enabled/disabled state, visibility, aliases, labels, voice assistant exposure, and entity_id rename in a single call.

BULK OPERATIONS: When entity_id is a list, only labels, expose_to, and categories parameters are supported. Other parameters (area_id, name, icon, device_class, options, enabled, hidden, aliases, use_entity_name_alias, new_entity_id, new_device_name) require single entity.

SHOW AS / DEVICE CLASS: a device_class change applies instantly, no reload needed.

REGISTRY OPTIONS: multi-domain options are sent as separate registry updates because HA's WS schema requires options_domain + options to be paired one domain at a time.

ENTITY ID RENAME: Voice exposure settings are preserved automatically.

WARNING: Renaming an entity_id does NOT update references in automations, scripts, templates, or dashboards. All consumers of the old entity_id must be updated manually — HA does not propagate the rename automatically.

Rename limitations:

  • Entity history is preserved (HA 2022.4+)

  • Entities without unique IDs cannot be renamed

  • Entities disabled by their integration cannot be renamed

DEVICE RENAME: new_device_name can be combined with new_entity_id to rename both in one call. The device is looked up automatically.

Use ha_search() or ha_get_device() to find entity IDs. Use ha_config_get_label() to find available label IDs.

EXAMPLES:

  • Assign to area: ha_set_entity("sensor.temp", area_id="living_room")

  • Set Show As: ha_set_entity("binary_sensor.zone_10", device_class="window")

  • Set sensor precision: ha_set_entity("sensor.power", options={"sensor": {"display_precision": 2}})

  • Rename entity and device: ha_set_entity("light.old", new_entity_id="light.new", new_device_name="New Lamp")

  • Add labels to multiple: ha_set_entity(["light.a", "light.b"], labels=["new"], label_operation="add")

  • Expose to Alexa: ha_set_entity("light.lamp", expose_to={"cloud.alexa": True})

ENABLED/DISABLED WARNING: A disabled entity will NOT appear in state queries, dashboards, or automations until re-enabled AND the integration is reloaded. This is NOT the same as "turning off" an entity.

For automations and scripts, enabled=False is blocked. For automations, use ha_config_set_automation(identifier="automation.xxx", enabled=False) (or enabled=True to re-enable). For scripts, ha_config_set_script(script_id="script.xxx", run="stop") only stops a currently running execution; it does not disable the script. Home Assistant has no script runtime enable/disable service.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
iconNoIcon for the entity (e.g., 'mdi:thermometer'). Use empty string '' to remove custom icon.
nameNoDisplay name for the entity. Use empty string '' to remove custom name and revert to default.
hiddenNoTrue to hide the entity from UI, False to show it.
labelsNoList of label IDs for the entity. Behavior depends on label_operation parameter. Use [] with label_operation='set' to clear.
aliasesNoList of voice assistant aliases for the entity (replaces existing aliases). A null entry is the entity's own name (HA's 'use entity name' switch); it is kept automatically unless your list already contains null. To turn that switch off or on, use use_entity_name_alias.
area_idNoArea/room ID to assign the entity to. Use empty string '' to unassign from current area.
enabledNoTrue to enable the entity, False to disable it. WARNING: Setting enabled=False is a registry-level disable — it completely removes the entity from the state machine and hides it from the UI. A reload or restart is required to restore it after re-enabling.
optionsNoPer-domain entity registry options (e.g. sensor 'display_precision', weather 'forecast_type'). Pass a dict mapping domain to a sub-dict, e.g. {"sensor": {"display_precision": 2}}. Multiple domains are sent as separate registry updates. For 'Show As' use device_class; for voice-assistant exposure use expose_to, not options.<assistant>.should_expose.
entity_idYesEntity ID or list of entity IDs to update.
expose_toNoControl voice assistant exposure. Pass a dict mapping assistant IDs to booleans. Valid assistants: 'conversation' (Assist), 'cloud.alexa', 'cloud.google_assistant'. Example: {"conversation": true, "cloud.alexa": false}.
categoriesNoCategory assignment as a dict mapping scope to category_id. Example: {"automation": "category_id_here"}. Use null value to clear: {"automation": null}.
device_classNoOverride the entity's display device class — what the HA UI's 'Show As' dropdown writes. Use empty string '' to clear the override and fall back to the integration default. Examples: 'window', 'door', 'motion' for binary_sensor; 'temperature', 'humidity' for sensor.
new_entity_idNoNew entity ID to rename to (e.g., 'light.new_name'). Domain must match the original.
label_operationNoHow to apply labels: 'set' replaces all labels, 'add' adds to existing, 'remove' removes specified labels.set
new_device_nameNoNew display name for the associated device. If provided, both entity and device are updated in one operation.
use_entity_name_aliasNoHA's 'use entity name' voice-alias switch. True keeps the entity's own name answering in Assist, False turns it off so only the aliases match. Omit to leave it as is. Works with or without aliases.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark it destructive/idempotent, but the description goes far beyond: rename does not propagate to automations/dashboards, history is preserved, entities without unique IDs or integration-disabled entities cannot be renamed, disabling removes the entity from the state machine until reload. These are high-value behavioral disclosures an agent cannot infer.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose followed by clearly headed sections (BULK, SHOW AS, REGISTRY OPTIONS, RENAME, WARNING, EXAMPLES). It is long but most content earns its place; the trailing Claude Desktop timeout guidance is operational noise that slightly dilutes focus.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 16-parameter destructive mutation tool with an output schema, the description covers bulk constraints, rename caveats, disable semantics, and cross-tool routing. Nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so baseline is 3, but the description adds the critical cross-parameter constraint that list-valued entity_id only supports labels/expose_to/categories, plus worked examples for options, expose_to, and label_operation. It adds real semantics beyond the per-param schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Update') and resource ('entity properties in the entity registry'), then enumerates the modifiable metadata (area, name, icon, device class, options, enabled, aliases, labels, exposure, rename). This clearly distinguishes it from ha_set_device and ha_config_set_label siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells the agent when bulk mode is allowed (only labels/expose_to/categories with a list entity_id), what is blocked (enabled=False for automations/scripts, with pointers to ha_config_set_automation and the absence of a script disable service), and how to find IDs via ha_search/ha_get_device.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_set_integrationSet IntegrationA
Destructive

Manage an integration (config entry): enable/disable, add, update options, or reconfigure.

Modes (pick one):

  • Enable/disable: entry_id + enabled.

  • Add integration: domain (+ config) — drives the domain's config flow, including menus and multi-step forms.

  • Update options: entry_id + config — drives the entry's options flow (what the "Configure" button does in the HA UI).

  • Reconfigure: entry_id + reconfigure=True + config — connection settings such as host, port, credentials. Repeat with the token the preflight returns as confirm_token to apply.

  • Log level: log_level + domain (or entry_id) — sets how much the integration logs; read it back with ha_get_integration's log_level.

WHEN NOT TO USE:

  • Helpers (template, group, utility_meter, ...): use ha_config_set_helper. The exception is otp, which is a helper in the HA UI but is created HERE via domain="otp" — its flow needs a live TOTP code, so ha_config_set_helper deliberately omits it.

  • Config subentries: use ha_config_set_helper(helper_type='config_subentry').

  • Removing an entry: use ha_remove_helpers_integrations.

Use ha_get_integration() to find entry IDs, and ha_get_integration(entry_id=..., include_schema=True) to inspect the options fields before an update. Its supports_reconfigure field tells you whether an entry qualifies for reconfigure=True.

Caveats: adding an integration runs its config flow exactly as the HA UI would (may pair devices, scan the network, create entities). Flows requiring a browser step (OAuth) or an asynchronous provider step error out at that step instead of completing. Reconfigure edits the settings a live integration connects with: a wrong host or credential takes it offline, and there is no automatic rollback — the returned rollback metadata describes repeating the official flow by hand with the previous values, which this tool cannot read back. The preflight does not validate config keys against the integration's form; wrong field names surface on the confirm call.

EXAMPLES:

  • Add: ha_set_integration(domain="workday", config={"name": "Workday"})

  • Update options: ha_set_integration(entry_id="abc123", config={"scan_interval": 30})

  • Reconfigure preflight: ha_set_integration(entry_id="abc123", reconfigure=True, config={"host": "10.0.0.5"}), then repeat adding confirm_token="sha256:..."

  • Debug logging: ha_set_integration(domain="zha", log_level="DEBUG"), then log_level="DEFAULT" to stop

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

integration config entry enable disable add options reconfigure log level debug logging

ParametersJSON Schema
NameRequiredDescriptionDefault
configNoFlow form data. Updating an existing entry — options or reconfigure — is a patch: a field you omit keeps its current value, and a field set to null is cleared where the integration's schema allows that field to be empty. Multi-step flows consume keys per step. A field two steps declare gets your one value both times; pass step_values={'<step_id>': {'<field>': <value>}} to give a step its own value, or to leave the field out of that step; a LIST of those objects supplies one per encounter when the flow presents a step more than once. Menu steps take 'next_step_id' — a string, or a list of successive selections for flows that present more than one menu (e.g. a menu revisited after each branch, ending in a finish option). The step's data_schema is returned on validation errors so field names can be corrected.
domainNoIntegration domain to add (e.g. 'workday', 'local_calendar').
enabledNoTrue to enable, False to disable the entry. Mutually exclusive with 'domain' and 'config'.
entry_idNoConfig entry ID of an existing integration.
log_levelNoSet the integration's log level, like the integration page's Enable/Disable debug logging: pass the integration with 'domain' (no config flow runs) or 'entry_id', and nothing else. Lasts through the next Home Assistant restart. DEFAULT clears the override: the integration then logs at the inherited default level, and a level set for it in configuration.yaml returns only after a restart.
reconfigureNoUse the existing config entry's official reconfigure flow (its connection settings) instead of its options flow. Without confirm_token this is a read-only preflight that returns one.
expected_macNoRequires reconfigure=True. MAC or IEEE the entry's device must still report.
confirm_tokenNoRequires reconfigure=True. Any token still matching the entry's current state and the same requested config is accepted, so a token stays valid while nothing moves.
expected_device_idNoRequires reconfigure=True. Device registry ID the entry must still own, before and after the change.
expected_unique_idNoRequires reconfigure=True, AND the ha_mcp_tools custom component: Home Assistant does not expose a config entry's unique_id over its API. Without the component this is rejected — anchor on expected_device_id, expected_mac or expected_entity_ids instead.
expected_entity_idsNoRequires reconfigure=True. Exact entity IDs that must remain associated with the entry.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare a destructive, non-idempotent, non-read-only write, but the description goes well beyond them by disclosing that reconfigure has no automatic rollback and can take a live integration offline, that OAuth/async flows error out mid-flow, that the preflight does not validate config keys, and that log levels persist until restart. It also documents a real-world timeout failure mode and recovery.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose and mode table, then usage exclusions, caveats, and examples, each in clearly labeled blocks. Though long, every section carries non-redundant information demanded by an 11-parameter mutation tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return-value documentation is not required, yet the description still explains the preflight/confirm_token round-trip and rollback metadata semantics. Combined with mode routing, caveats, and examples, an agent has everything needed to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema descriptions are themselves very detailed, so baseline is 3. The description adds value above that by mapping parameter combinations to modes (entry_id+enabled, domain+config, entry_id+reconfigure+config, log_level+domain), clarifying mutual exclusivity, and demonstrating usage through worked examples.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Manage an integration (config entry)') and immediately enumerates the concrete operations it covers (enable/disable, add, update options, reconfigure). It explicitly names the siblings it is not (ha_config_set_helper, ha_remove_helpers_integrations, ha_get_integration), so an agent can distinguish it without inspecting other schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Contains an explicit WHEN NOT TO USE section routing helpers to ha_config_set_helper, subentries to a helper_type, and removal to ha_remove_helpers_integrations, including the notable 'otp' exception. The Modes list states exactly which parameter combination selects each operation, and it points to ha_get_integration for pre-reads.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_set_todo_itemSet Todo ItemA
Destructive

Create or update a todo item in Home Assistant.

Without item (create mode): creates a new item; summary is required. With item (update mode): updates that item; at least one of rename, status, description, due_date or due_datetime is required.

EXAMPLES:

  • Add item: ha_set_todo_item("todo.tasks", summary="Pay bills", due_date="2024-12-31")

  • Complete item: ha_set_todo_item("todo.shopping_list", item="Buy milk", status="completed")

  • Rename item: ha_set_todo_item("todo.tasks", item="Old task", rename="New task name")

Not all todo integrations support all features: the Shopping List integration supports summary and status but not descriptions or due dates.

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
itemNoExisting item to update - can be the item UID or the exact item summary/name.
renameNoNew name/summary for an existing item. Only used in update mode.
statusNoItem status: 'completed' to mark done, 'needs_action' to mark incomplete. Only used in update mode.
summaryNoItem text/name. Ignored in update mode — use 'rename' to change the item name.
due_dateNoDue date in YYYY-MM-DD format (e.g., '2024-12-25')
entity_idYesTodo list entity ID (e.g., 'todo.shopping_list')
descriptionNoDetailed description for the item
due_datetimeNoDue datetime in ISO format (e.g., '2024-12-25T14:00:00'). Overrides due_date if both provided.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations mark this destructive (mutation), and the description adds genuinely useful operational context: per-integration feature support limits, and a specific timeout failure mode with remediation guidance (read the target back before repeating, adjust the manual-approve button / Always allow). It does not explain what an update overwrites or how rename interacts with existing summary state beyond the mode gating.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Core mode logic is front-loaded in two short sentences, then examples, then caveats. The Claude Desktop timeout paragraph is lengthy and unusually specific, but it carries actionable information rather than filler. Slightly verbose overall but well structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 8-parameter mutation tool with an output schema and destructive annotation, the description covers the mode requirements, integration-specific limitations, and a real-world timeout pitfall. What it omits (entity_id validation, behavior when `item` matches multiple entries, whether rename clears due dates) is minor relative to what it supplies.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description goes beyond individual field docs by explaining mode-dependent parameter requirements and showing three concrete call examples (add, complete, rename) that clarify how `item`, `summary`, and `rename` interact. The 'summary is ignored in update mode' distinction is reinforced at the call-shape level.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (create or update) and resource (todo item in Home Assistant), and explicitly decomposes the tool into create mode vs update mode based on presence of `item`. This distinguishes it cleanly from siblings like ha_get_todo and ha_remove_todo_item without needing to open the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit conditions for each mode: without `item`, `summary` is required; with `item`, at least one of rename/status/description/due_date/due_datetime is required. It also warns that not all todo integrations support all features (Shopping List supports summary/status but not descriptions or due dates). It does not name alternative tools for deletion or reading, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ha_set_zoneSet ZoneA
Destructive

Create or update a Home Assistant zone.

Omit zone_id to create a new zone (name, latitude, longitude required). Provide zone_id to update an existing zone (only specified fields change). The 'home' zone is typically defined in YAML and cannot be modified here.

EXAMPLES:

  • Create: ha_set_zone(name="Office", latitude=40.7128, longitude=-74.0060, radius=150, icon="mdi:briefcase")

  • Update: ha_set_zone(zone_id="abc123", radius=200)

If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, or to set this tool to Always allow.

ParametersJSON Schema
NameRequiredDescriptionDefault
iconNoMaterial Design Icon (e.g., 'mdi:briefcase', 'mdi:school')
nameNoDisplay name for the zone
radiusNoRadius of the zone in meters (must be > 0, defaults to 100 on create)
passiveNoPassive mode - if True, zone will not trigger enter/exit automations (defaults to False on create)
zone_idNoZone ID to update (use ha_get_zone to find IDs)
latitudeNoLatitude coordinate of the zone center
longitudeNoLongitude coordinate of the zone center

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only give destructiveHint=true and openWorldHint=false; the description adds genuinely non-redundant behavior: partial-update semantics ('only specified fields change'), the immutability of the home zone, and a timeout/repeat-call caveat on Claude Desktop. It stops short of saying what happens to the zone's other fields or what the write returns, so it misses the top mark.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Create/update rules and the home-zone caveat are front-loaded and terse, and the examples are compact. The final Claude Desktop timeout paragraph is longer and more operational-tooling-flavored than the rest, diluting an otherwise tight definition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter write tool with an output schema already defined, the description covers both modes, required-field differences, the one immovable object, and worked examples—everything an agent needs to invoke it correctly without guessing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline would be 3; the description earns extra by stating which fields are required in each mode (name/latitude/longitude on create) and by supplying two concrete call examples with realistic values. It adds meaning beyond the per-field schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Create or update a Home Assistant zone') and immediately splits the two modes (create vs. update) that the sibling set separates into ha_get_zone/ha_remove_zone. An agent knows exactly what this tool does without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit conditional routing: omit zone_id to create, provide zone_id to update, and a hard exclusion ('the home zone is typically defined in YAML and cannot be modified here'). It also points at ha_get_zone for discovering IDs, which is the concrete alternative for the lookup step.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 69 tool updatesv8.6.0
    • Changedha_bulk_control5 fields changed
      • addedInput schema / properties / dry_run / description
        Added value: +"Selector mode only; True is rejected in operations mode."
      • changedInput schema / properties / operations / description
        Previous value: -"Explicit entity operations. Use this or selector, never both. Each item requires exact entity_id and action. Use action='off', not service='turn_off'."New value: +"Explicit entity operations. Each item requires exact entity_id and action. Use action='off', not service='turn_off'."
      • addedInput schema / properties / parallel / description
        Added value: +"Dispatch operations concurrently (default) or one at a time when False."
      • addedInput schema / properties / timeout_seconds / description
        Added value: +"Selector mode only: confirmation wait in seconds for every resolved entity. In operations mode set timeout_seconds on each operation instead; a top-level value is rejected in operations mode."
      • addedInput schema / properties / validate_first / description
        Added value: +"Selector mode only: report ENTITY_NOT_FOUND for a target that does not exist. In operations mode set validate_first on each operation instead; a top-level False is rejected in operations mode."
    • Changedha_call_event2 fields changed
      • addedInput schema / properties / data / description
        Added value: +"Optional event payload, delivered to subscribers as the event's data (trigger.event.data in an automation)."
      • addedInput schema / properties / event_type / description
        Added value: +"Event type to fire, e.g. 'my_custom_event'."
    • Changedha_call_service8 fields changed
      • changedInput schema / properties / domain / description
        Previous value: -"Service domain (e.g. 'light', 'climate', 'automation'). Required for a service call; must be omitted when ws_command is set."New value: +"Service domain (e.g. 'light', 'climate', 'automation'). Required for a service call."
      • changedInput schema / properties / entity_id / description
        Previous value: -"Entity ID(s) the service call targets — one ID ('light.living_room') or several comma-separated ('light.a,light.b'). Optional for services that don't target a specific entity. Must be omitted when ws_command is set."New value: +"Entity ID(s) the service call targets — one ID ('light.living_room') or several comma-separated ('light.a,light.b'). Optional for services that don't target a specific entity."
      • changedInput schema / properties / result_attribute_keys / description
        Previous value: -"Project each record's 'attributes' dict to only these keys (e.g. ['brightness', 'rgb_color']). Mirrors ha_get_state's attribute_keys=. Setting this DISABLES default compaction. Requires 'attributes' to be present in result_fields (or result_fields=None)."New value: +"Project each record's 'attributes' dict to only these keys (e.g. ['brightness', 'rgb_color']). Setting this DISABLES default compaction. Requires 'attributes' to be present in result_fields (or result_fields=None)."
      • changedInput schema / properties / result_fields / description
        Previous value: -"Project each record in 'result' to only these top-level keys (e.g. ['entity_id', 'state']). Mirrors ha_get_state's fields=. Setting this DISABLES default compaction — no entity-id filter, no metadata strip — and applies the explicit projection instead."New value: +"Project each record in 'result' to only these top-level keys (e.g. ['entity_id', 'state']). Setting this DISABLES default compaction — no entity-id filter, no metadata strip — and applies the explicit projection instead."
      • changedInput schema / properties / return_response / description
        Previous value: -"If True, the service's response data is returned once, as the top-level 'service_response' key — never nested inside 'result' (default: False). Must stay False when ws_command is set."New value: +"If True, the service's response data is returned once, as the top-level 'service_response' key — never nested inside 'result'."
      • changedInput schema / properties / service / description
        Previous value: -"Service name within domain (e.g. 'turn_on', 'set_temperature', 'trigger'). Required for a service call; must be omitted when ws_command is set."New value: +"Service name within domain (e.g. 'turn_on', 'set_temperature', 'trigger'). Required for a service call."
      • changedInput schema / properties / verbose / description
        Previous value: -"Return HA's raw changed-state records unchanged (default: False). Use as an escape hatch when you need the full propagation chain or raw attribute payload (debug / inspection). With return_response=True the response data still surfaces once as the top-level service_response key, never nested in result. WARNING: brings back token-bloat for nested-group targets — prefer result_fields / result_attribute_keys for targeted control."New value: +"Return HA's raw changed-state records unchanged. With return_response=True the response data still surfaces once as the top-level service_response key, never nested in result. Large for nested-group targets — prefer result_fields / result_attribute_keys."
      • changedInput schema / properties / ws_command / description
        Previous value: -"Advanced escape hatch: send a raw one-shot Home Assistant WebSocket command that is NOT a registered service (e.g. 'repairs/ignore_issue' to dismiss a Repairs issue). When set, omit domain/service and the other service params; put the command's parameters in data. Streaming/two-phase and service-invoking commands (call_service, execute_script) are rejected."New value: +"Advanced escape hatch: send a raw one-shot Home Assistant WebSocket command that is NOT a registered service and that no dedicated tool covers. When set, omit domain/service and the other service params. Streaming/two-phase and service-invoking commands (call_service, execute_script) are rejected."
    • Changedha_config_delete_dashboard_resource1 field changed
      • changedInput schema / properties / resource_id / description
        Previous value: -"Resource ID to delete. Get from ha_config_list_dashboard_resources()"New value: +"Resource ID to delete."
    • Changedha_config_get_calendar_events1 field changed
      • changedInput schema / properties / end / description
        Previous value: -"End datetime in ISO format (default: 7 days from start)"New value: +"End datetime in ISO format (default: 7 days from now, not from start; pass end whenever you pass start)"
    • Changedha_config_get_category1 field changed
      • changedInput schema / properties / category_id / description
        Previous value: -"ID of the category to retrieve. If omitted, lists all categories for the scope."New value: +"ID of the category to retrieve."
    • Changedha_config_get_dashboard10 fields changed
      • changedInput schema / properties / card_type / description
        Previous value: -"Find cards by type, e.g. 'tile', 'button', 'heading'. When provided, activates search mode."New value: +"Find cards by type, e.g. 'tile', 'button', 'heading'."
      • changedInput schema / properties / entity_id / description
        Previous value: -"Find cards by entity ID. Supports wildcards, e.g. 'sensor.temperature_*'. Matches cards with this entity in 'entity' or 'entities' field, view-level badges, and header cards. When provided, activates search mode (returns matches, not full config)."New value: +"Find cards by entity ID. Supports wildcards, e.g. 'sensor.temperature_*'. Matches cards with this entity in 'entity' or 'entities' field, view-level badges, and header cards."
      • changedInput schema / properties / force_reload / description
        Previous value: -"Force reload from storage (bypass cache). Not applicable in search mode, which always reads fresh config."New value: +"Force reload from storage (bypass cache). Not applicable in search mode."
      • changedInput schema / properties / heading / description
        Previous value: -"Find cards by heading/title text (case-insensitive partial match). When provided, activates search mode."New value: +"Find cards by heading/title text (case-insensitive partial match)."
      • changedInput schema / properties / include_config / description
        Previous value: -"In search mode: include each matched card's own configuration object in results (increases output size). Note that a matched container card's config contains its descendants, which are themselves separate matches with their own config, so deeply-nested stacks multiply the payload — keep the default (False) unless you need the bodies. Does not affect whether the full dashboard config is returned — search mode always returns matches only, not the full dashboard. Config bodies are surfaced only for dashboards provably in storage mode; for a YAML or unconfirmed dashboard the bodies are withheld (they may carry resolved !secret values) and the response says so, with match locations still reported. Ignored outside search mode."New value: +"In search mode: include each matched card's own configuration object in results. A container card's body includes its descendants, which are also separate matches with their own bodies, so nested stacks multiply the payload. Bodies are returned only for dashboards provably in storage mode; for a YAML or unconfirmed dashboard they are withheld (they may carry resolved !secret values) and the response says so, with match locations still reported. Ignored outside search mode."
      • changedInput schema / properties / include_screenshot / description
        Previous value: -"Get mode only: also return rendered image(s) of the dashboard for visual verification. Requires the 'dashboard screenshot' beta feature + engine add-on/sidecar. If the feature is disabled the config is returned with a warning; if the engine is configured but the render fails, the call errors (the screenshot is the requested payload). Ignored in list/search mode. When you already have the config and only need the render, use the dedicated ha_get_dashboard_screenshot tool (registered when the same beta feature is on) — it returns images without echoing the config."New value: +"Get mode only: also return rendered image(s) of the dashboard for visual verification. Requires the 'dashboard screenshot' beta feature + engine app (add-on)/sidecar. If the feature is disabled the config is returned with a warning; if the engine is configured but the render fails, the call errors. Ignored in list/search mode."
      • changedInput schema / properties / list_only / description
        Previous value: -"If True, list all dashboards instead of getting config. When True, url_path is ignored."New value: +"When True, url_path is ignored."
      • changedInput schema / properties / mode / description
        Previous value: -"Set to 'search' for a CROSS-dashboard search: which dashboards contain a given entity_id or text (requires query). Leave unset for the default list/get/single-dashboard-search behavior selected by list_only / entity_id / card_type / heading."New value: +"Set to 'search' (requires query)."
      • changedInput schema / properties / url_path / description
        Previous value: -"Dashboard URL path (e.g., 'lovelace-home'). Use 'default' for default dashboard. If omitted with list_only=True, lists all dashboards."New value: +"Dashboard URL path (e.g., 'lovelace-home'). Use 'default' for default dashboard."
      • changedInput schema / properties / view_path / description
        Previous value: -"Get mode: return ONLY the view whose Lovelace views[].path matches (response carries 'view' + 'view_index' instead of the full 'config') — use this to keep multi-view dashboards from blowing up the response when you only need one view. Does not require any beta feature. With include_screenshot, also selects the view to render. Ignored in list/search mode. Omit for the full config."New value: +"Get mode: return ONLY the view whose Lovelace views[].path matches (response carries 'view' + 'view_index' instead of the full 'config'). With include_screenshot, also selects the view to render. Ignored in list/search mode."
    • Changedha_config_get_label1 field changed
      • changedInput schema / properties / label_id / description
        Previous value: -"ID of the label to retrieve. If omitted, lists all labels."New value: +"ID of the label to retrieve."
    • Changedha_config_get_scene9 fields changed
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 20,
        +  "description": "Maximum scenes per page",
        +  "maximum": 100,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • addedInput schema / properties / offset
        Added value: +{
        +  "default": 0,
        +  "description": "Pagination offset",
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • addedInput schema / properties / query
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Filter scene names or IDs"
        +}
      • addedInput schema / properties / scene_id / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / scene_id / default
        Added value: +null
      • changedInput schema / properties / scene_id / description
        Previous value: -"Scene identifier (e.g., 'movie_night')"New value: +"Scene storage ID from listing (e.g. 'movie_night') or the scene's entity_id; an entity_id is resolved to the storage ID."
      • removedInput schema / properties / scene_id / type
        Removed value: -"string"
      • addedInput schema / properties / search_in_config
        Added value: +{
        +  "default": false,
        +  "description": "Also search full stored scene attribute values within a bounded scan",
        +  "type": "boolean"
        +}
      • removedInput schema / required
        Removed value: -[
        -  "scene_id"
        -]
    • Changedha_config_list_dashboard_resources3 fields changed
      • changedInput schema / properties / include_content / description
        Previous value: -"Include full decoded content for inline resources. Default False to save tokens (shows 150-char preview instead)."New value: +"Include full decoded content for inline resources in the \"_content\" field. When False, a 150-char preview is shown instead. Rows past the per-response content budget carry '_content_truncated': True instead of '_content'; request a smaller page before copying content into an update."
      • changedInput schema / properties / limit / description
        Previous value: -"Max resources to return per page (default: 100)"New value: +"Max resources to return per page"
      • changedInput schema / properties / offset / description
        Previous value: -"Number of resources to skip for pagination (default: 0)"New value: +"Number of resources to skip for pagination"
    • Changedha_config_list_groups2 fields changed
      • changedInput schema / properties / limit / description
        Previous value: -"Max groups to return per page (default: 100)"New value: +"Max groups to return per page"
      • changedInput schema / properties / offset / description
        Previous value: -"Number of groups to skip for pagination (default: 0)"New value: +"Number of groups to skip for pagination"
    • Changedha_config_list_helpers2 fields changed
      • changedInput schema / properties / limit / description
        Previous value: -"Max helpers to return per page (default: 100)"New value: +"Max helpers to return per page"
      • changedInput schema / properties / offset / description
        Previous value: -"Number of helpers to skip for pagination (default: 0)"New value: +"Number of helpers to skip for pagination"
    • Changedha_config_remove_automation1 field changed
      • changedInput schema / properties / wait / description
        Previous value: -"Wait for automation to be fully removed before returning. Default: True."New value: +"Wait for automation to be fully removed before returning."
    • Changedha_config_remove_calendar_event3 fields changed
      • changedInput schema / properties / recurrence_id / description
        Previous value: -"Optional recurrence ID for recurring events"New value: +"Recurrence ID for recurring events"
      • changedInput schema / properties / recurrence_range / anyOf
        Previous value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "const": "THISANDFUTURE",
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / recurrence_range / description
        Previous value: -"Optional recurrence range ('THIS_AND_FUTURE' to delete this and future occurrences)"New value: +"Recurrence range: 'THISANDFUTURE' to delete this and future occurrences. Home Assistant compares this value verbatim, so no other spelling (including 'THIS_AND_FUTURE') selects the range."
    • Changedha_config_remove_group1 field changed
      • changedInput schema / properties / wait / description
        Previous value: -"Wait for group to be fully removed before returning. Default: True."New value: +"Wait for group to be fully removed before returning."
    • Changedha_config_remove_scene1 field changed
      • changedInput schema / properties / wait / description
        Previous value: -"Wait for scene to be fully removed before returning. Default: True."New value: +"Wait for scene to be fully removed before returning."
    • Changedha_config_remove_script1 field changed
      • changedInput schema / properties / wait / description
        Previous value: -"Wait for script to be fully removed before returning. Default: True."New value: +"Wait for script to be fully removed before returning."
    • Changedha_config_set_automation8 fields changed
      • changedInput schema / properties / config / description
        Previous value: -"Complete automation configuration with required fields: 'alias', 'triggers', 'actions'. Optional: 'description', 'conditions', 'mode', 'max', 'initial_state', 'variables'. Purpose-specific triggers/conditions (HA 2026.7+ default: 'trigger': '<domain>.<name>' with 'target'/'options') are valid config. Mutually exclusive with python_transform."New value: +"Complete automation configuration. Purpose-specific triggers/conditions (HA 2026.7+ default: 'trigger': '<domain>.<name>' with 'target'/'options') are valid config. Mutually exclusive with python_transform."
      • changedInput schema / properties / config_hash / description
        Previous value: -"Config hash from ha_config_get_automation for optimistic locking. REQUIRED for python_transform (validates automation unchanged). Required when a config update changes an existing automation's alias. Otherwise optional for config updates (validates before full replacement if provided)."New value: +"Config hash from ha_config_get_automation for optimistic locking. Required for python_transform and when a config update changes an existing automation's alias; otherwise optional for config updates (validates before full replacement if provided)."
      • addedInput schema / properties / enabled
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "boolean"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Turn the automation on (True) or off (False) after an optional config update; None leaves it unchanged. Not written into the stored config: Home Assistant keeps the state across restarts, but a config 'initial_state' overrides it whenever the automation is reloaded or HA starts."
        +}
      • changedInput schema / properties / identifier / description
        Previous value: -"Target automation entity_id or HA config 'id' (unique_id). Omit for creation with a generated ID. Values such as 'new' are literal IDs, not placeholders. Required for python_transform."New value: +"Target automation entity_id or HA config 'id' (unique_id). Omit for creation with a generated ID. Values such as 'new' are literal IDs, not placeholders."
      • changedInput schema / properties / python_transform / description
        Previous value: -"Python expression to transform existing automation config. Mutually exclusive with config. Requires identifier and config_hash for validation. WARNING: Expressions with infinite loops will hang the server. Examples: Simple: python_transform=\"config['actions'][0]['data']['brightness'] = 255\" Pattern: python_transform=\"for a in config['actions']: if a.get('alias') == 'My Step': a['data']['value'] = 100\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead\n\n🎯 PATTERNS:\n- Filter cards: cards = [c for c in cards if keep(c)]\n- Skip in a loop: prefer `continue` over an empty `pass` branch (clearer)\n- Conditionally include: build a new list and `.append(x)` only the\n  cards you want, instead of iterating the original and using if/pass\n  branches to drop entries\n- Modify in place when possible (single pass, fewer surprises) over\n  reconstructing the entire list"New value: +"Python expression to transform existing automation config. Mutually exclusive with config. WARNING: Expressions with infinite loops will hang the server. Examples: Simple: python_transform=\"config['actions'][0]['data']['brightness'] = 255\" Pattern: python_transform=\"for a in config['actions']: if a.get('alias') == 'My Step': a['data']['value'] = 100\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead"
      • addedInput schema / properties / run_actions
        Added value: +{
        +  "default": false,
        +  "description": "Run the automation's actions now, skipping its triggers and conditions (automation.trigger, the UI's Run actions). Applied after `enabled` and after any config write. Can be used standalone with identifier and no config.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / take_control_of_blueprint / description
        Previous value: -"Convert a blueprint-backed automation into an editable standalone one -- the UI's \"Take control\". Renders the blueprint with its current inputs and saves the result over the same automation, which then has its own triggers/conditions/actions and no 'use_blueprint'. Requires identifier; mutually exclusive with config and python_transform. Irreversible: the link to the blueprint is gone afterwards, so edit inputs instead if you only want to change a value. Does NOT free the blueprint: Home Assistant keeps counting the converted automation as a user, so deleting that blueprint stays refused until the automation is removed. To preview the rendering without writing anything, use ha_manage_blueprints(action=\"substitute\")."New value: +"Convert a blueprint-backed automation into an editable standalone one -- the UI's \"Take control\". Renders the blueprint with its current inputs and saves the result over the same automation, which keeps its entity_id, alias and description and then has its own triggers/conditions/actions and no 'use_blueprint'. Requires identifier; mutually exclusive with config and python_transform."
      • changedInput schema / properties / wait / description
        Previous value: -"Wait for automation to be queryable before returning. Default: True. Set to False for bulk operations."New value: +"Wait for automation to be queryable before returning. Set to False for bulk operations."
    • Changedha_config_set_calendar_event6 fields changed
      • changedInput schema / properties / description / description
        Previous value: -"Optional event description"New value: +"Event description"
      • changedInput schema / properties / location / description
        Previous value: -"Optional event location"New value: +"Event location"
      • addedInput schema / properties / recurrence_id
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Only meaningful with 'uid': identifies one occurrence of a recurring series to update."
        +}
      • addedInput schema / properties / recurrence_range
        Added value: +{
        +  "anyOf": [
        +    {
        +      "const": "THISANDFUTURE",
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Only meaningful with 'uid': 'THISANDFUTURE' to update this and all following occurrences. Home Assistant compares this value verbatim, so no other spelling (including 'THIS_AND_FUTURE') selects the range."
        +}
      • changedInput schema / properties / rrule / description
        Previous value: -"Optional RFC 5545 recurrence rule, without 'RRULE:' prefix (e.g., 'FREQ=WEEKLY;BYDAY=MO' or 'FREQ=MONTHLY;BYDAY=3SA'). Creates a recurring event series."New value: +"RFC 5545 recurrence rule, without 'RRULE:' prefix (e.g., 'FREQ=WEEKLY;BYDAY=MO' or 'FREQ=MONTHLY;BYDAY=3SA'). Creates a recurring event series."
      • addedInput schema / properties / uid
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "UID of an existing event to update. Omit to create a new event."
        +}
    • Changedha_config_set_category1 field changed
      • changedInput schema / properties / category_id / description
        Previous value: -"Category ID for updates. If not provided, creates a new category."New value: +"Category ID for updates."
    • Changedha_config_set_dashboard3 fields changed
      • changedInput schema / properties / patch / description
        Previous value: -"Structured dashboard edits: up to 100 JSON Patch add, remove, replace or test operations using RFC 6901 paths. Use /- to append to an array; escape ~ as ~0 and / as ~1 in keys. Requires config_hash. Mutually exclusive with config and python_transform. Update title/icon/require_admin/show_in_sidebar in a separate call. Strings in value are preserved literally."New value: +"Structured dashboard edits: up to 100 JSON Patch add, remove, replace or test operations using RFC 6901 paths. Use /- to append to an array; escape ~ as ~0 and / as ~1 in keys. Mutually exclusive with config and python_transform. Strings in value are preserved literally."
      • changedInput schema / properties / python_transform / description
        Previous value: -"Python expression to transform existing dashboard config. Mutually exclusive with config and patch. Requires config_hash for validation. See PYTHON TRANSFORM SECURITY below for allowed operations. Examples: Simple: python_transform=\"config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'\" Pattern: python_transform=\"for card in config['views'][0]['cards']: if 'light' in card.get('entity', ''): card['icon'] = 'mdi:lightbulb'\" Multi-op: python_transform=\"config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'; del config['views'][0]['cards'][2]\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead\n\n🎯 PATTERNS:\n- Filter cards: cards = [c for c in cards if keep(c)]\n- Skip in a loop: prefer `continue` over an empty `pass` branch (clearer)\n- Conditionally include: build a new list and `.append(x)` only the\n  cards you want, instead of iterating the original and using if/pass\n  branches to drop entries\n- Modify in place when possible (single pass, fewer surprises) over\n  reconstructing the entire list"New value: +"Python expression to transform existing dashboard config. Mutually exclusive with config and patch. Requires config_hash for validation. See PYTHON TRANSFORM SECURITY below for allowed operations. Examples: Simple: python_transform=\"config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'\" Pattern: python_transform=\"for card in config['views'][0]['cards']: if 'light' in card.get('entity', ''): card['icon'] = 'mdi:lightbulb'\" Multi-op: python_transform=\"config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'; del config['views'][0]['cards'][2]\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead"
      • changedInput schema / properties / return_screenshot / description
        Previous value: -"After writing, also return rendered image(s) of the dashboard so you can see what it looks like in a single call (the dashboard creation/iteration loop). Requires the 'dashboard screenshot' beta feature + engine add-on/sidecar; if unavailable, the write result is returned with a warning. For visual re-checks after the write (no config round-trip), use the dedicated ha_get_dashboard_screenshot tool instead."New value: +"After writing, also return rendered image(s) of the dashboard. Requires the 'dashboard screenshot' beta feature + engine app (add-on)/sidecar; if unavailable, the write result is returned with a warning."
    • Changedha_config_set_dashboard_resource3 fields changed
      • changedInput schema / properties / content / description
        Previous value: -"JavaScript or CSS code to host inline (max ~128KB). The code is embedded directly in the resource URL as a data: URI - no file storage or external hosting involved. Mutually exclusive with url. Supports 'module' and 'css' types only."New value: +"JavaScript or CSS code to host inline."
      • changedInput schema / properties / resource_type / description
        Previous value: -"Resource type: 'module' for ES6 modules (modern cards, default), 'js' for legacy JavaScript (url mode only), 'css' for stylesheets"New value: +"Resource type: 'module' for ES6 modules (modern cards), 'js' for legacy JavaScript (older custom cards), 'css' for stylesheets (themes, global styles)"
      • changedInput schema / properties / url / description
        Previous value: -"URL of the resource. Can be: /local/file.js (www/ directory), /hacsfiles/component/file.js (HACS), https://cdn.example.com/card.js (external). Mutually exclusive with content."New value: +"URL of the resource."
    • Changedha_config_set_group5 fields changed
      • changedInput schema / properties / add_entities / description
        Previous value: -"Add these entities to an existing group (mutually exclusive with entities)"New value: +"Add these entities to an existing group"
      • changedInput schema / properties / all_on / description
        Previous value: -"If True, all entities must be on for group to be on (default: False)"New value: +"If True, all entities must be on for group to be on"
      • changedInput schema / properties / entities / description
        Previous value: -"List of entity IDs for the group. Required when creating new group. When updating, replaces all entities (mutually exclusive with add_entities/remove_entities)."New value: +"List of entity IDs for the group. When updating, replaces all entities."
      • changedInput schema / properties / remove_entities / description
        Previous value: -"Remove these entities from an existing group (mutually exclusive with entities)"New value: +"Remove these entities from an existing group"
      • changedInput schema / properties / wait / description
        Previous value: -"Wait for group to be queryable before returning. Default: True. Set to False for bulk operations."New value: +"Wait for group to be queryable before returning. Set to False for bulk operations."
    • Changedha_config_set_helper13 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Explicit intent: 'create' a new helper or 'update' an existing one. When omitted, falls back to the implicit discriminator: presence of helper_id => update, absence => create. Pass 'create' or 'update' to disambiguate (e.g. so a typo in helper_id surfaces as a clear 'helper not found' error instead of being mistaken for a create call)."New value: +"Explicit intent: 'create' a new helper or 'update' an existing one. Pass it so a helper_id typo fails as 'helper not found' instead of creating a helper."
      • changedInput schema / properties / config / description
        Previous value: -"Config dict for flow-based helper types and helper_type='config_subentry' (template, group, utility_meter, derivative, min_max, threshold, integration, statistics, trend, random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat, history_stats, mold_indicator). Ignored for simple helper types. On update it is a patch: a field you omit keeps its current value, and a field set to null is cleared where the schema allows that field to be empty. A field two steps declare gets your one value both times; pass step_values={'<step_id>': {'<field>': <value>}} to give a step its own value, or to leave it out of that step; a LIST of those objects supplies one per encounter when the flow presents a step more than once. Field set is delivered as data_schema on the first validation error."New value: +"Config dict for flow-based helper types and helper_type='config_subentry'. Ignored for simple helper types. On update it is a patch: a field you omit keeps its current value, and a field set to null is cleared where the schema allows that field to be empty. A field two steps declare gets your one value both times; pass step_values={'<step_id>': {'<field>': <value>}} to give a step its own value, or to leave it out of that step; a LIST of those objects supplies one per encounter when the flow presents a step more than once."
      • changedInput schema / properties / friday / description
        Previous value: -"Schedule time ranges for Friday. List of {'from': 'HH:MM', 'to': 'HH:MM'} dicts. Optional 'data' dict for additional attributes."New value: +"Schedule time ranges for Friday; same shape as monday."
      • changedInput schema / properties / helper_id / description
        Previous value: -"REQUIRED when updating an existing helper. Bare ID ('my_button') or full entity ID ('input_button.my_button'). Omit to create a new helper."New value: +"Bare ID ('my_button') or full entity ID ('input_button.my_button'). Omit to create a new helper."
      • changedInput schema / properties / name / description
        Previous value: -"Display name for simple/flow helper creation. Required when creating a helper without helper_id. Optional on helper update. Ignored for helper_type='config_subentry', which uses entry_id/subentry_type/subentry_id instead. For flow-based helper updates (template, group, utility_meter, ...), this is typically ignored because options flows don't expose renaming. Rename a flow helper by deleting and recreating instead."New value: +"Display name for simple/flow helper creation. Optional on helper update. Ignored for helper_type='config_subentry', which uses entry_id/subentry_type/subentry_id instead. For flow-based helper updates (template, group, utility_meter, ...), this is ignored because options flows don't expose renaming; change the resulting entity's display name with ha_set_entity(name=...)."
      • changedInput schema / properties / saturday / description
        Previous value: -"Schedule time ranges for Saturday. List of {'from': 'HH:MM', 'to': 'HH:MM'} dicts. Optional 'data' dict for additional attributes."New value: +"Schedule time ranges for Saturday; same shape as monday."
      • changedInput schema / properties / subentry_id / description
        Previous value: -"Existing config subentry ID to reconfigure when helper_type='config_subentry'. Omit to create."New value: +"Existing config subentry ID to reconfigure when helper_type='config_subentry'."
      • changedInput schema / properties / sunday / description
        Previous value: -"Schedule time ranges for Sunday. List of {'from': 'HH:MM', 'to': 'HH:MM'} dicts. Optional 'data' dict for additional attributes."New value: +"Schedule time ranges for Sunday; same shape as monday."
      • changedInput schema / properties / tag_id / description
        Previous value: -"Tag ID for tag. On create, omit to auto-generate a unique uuid4 hex (HA's tag/create requires this field; the tool fills it in for you). On update, the tag's existing tag_id is required (passed via helper_id)."New value: +"Tag ID. On create, omit to auto-generate a uuid4 hex. On update, the existing tag_id is required (passed via helper_id)."
      • changedInput schema / properties / thursday / description
        Previous value: -"Schedule time ranges for Thursday. List of {'from': 'HH:MM', 'to': 'HH:MM'} dicts. Optional 'data' dict for additional attributes."New value: +"Schedule time ranges for Thursday; same shape as monday."
      • changedInput schema / properties / tuesday / description
        Previous value: -"Schedule time ranges for Tuesday. List of {'from': 'HH:MM', 'to': 'HH:MM'} dicts. Optional 'data' dict for additional attributes."New value: +"Schedule time ranges for Tuesday; same shape as monday."
      • changedInput schema / properties / wait / description
        Previous value: -"Wait for helper entity to be queryable before returning. Default: True. Set to False for bulk operations."New value: +"Wait for helper entity to be queryable before returning. Set to False for bulk operations."
      • changedInput schema / properties / wednesday / description
        Previous value: -"Schedule time ranges for Wednesday. List of {'from': 'HH:MM', 'to': 'HH:MM'} dicts. Optional 'data' dict for additional attributes."New value: +"Schedule time ranges for Wednesday; same shape as monday."
    • Changedha_config_set_label2 fields changed
      • addedInput schema / properties / areas
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Area IDs to apply this label to (adds the label without removing existing ones). Omit to leave area assignments unchanged; an empty list is a no-op (assigns nothing and removes nothing). To clear an area's labels use ha_set_area_or_floor(kind='area', labels=[])."
        +}
      • changedInput schema / properties / label_id / description
        Previous value: -"Label ID for updates. If not provided, creates a new label."New value: +"Label ID for updates."
    • Changedha_config_set_scene3 fields changed
      • addedInput schema / properties / activate
        Added value: +{
        +  "default": false,
        +  "description": "Activate the scene (scene.turn_on). Alone with scene_id it activates any scene entity, including integration and YAML scenes (pass their entity_id). With config or python_transform it runs after Home Assistant has reloaded scenes from the write.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / python_transform / description
        Previous value: -"Python expression to transform existing scene config. Mutually exclusive with config. Requires config_hash for validation. WARNING: Expressions with infinite loops will hang the server. Examples: Add entity: python_transform=\"config['entities']['light.bed'] = {'state': 'on'}\" Update brightness: python_transform=\"config['entities']['light.kitchen']['brightness'] = 50\" Remove entity: python_transform=\"del config['entities']['light.kitchen']\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead\n\n🎯 PATTERNS:\n- Filter cards: cards = [c for c in cards if keep(c)]\n- Skip in a loop: prefer `continue` over an empty `pass` branch (clearer)\n- Conditionally include: build a new list and `.append(x)` only the\n  cards you want, instead of iterating the original and using if/pass\n  branches to drop entries\n- Modify in place when possible (single pass, fewer surprises) over\n  reconstructing the entire list"New value: +"Python expression to transform existing scene config. Mutually exclusive with config. WARNING: Expressions with infinite loops will hang the server. Examples: Add entity: python_transform=\"config['entities']['light.bed'] = {'state': 'on'}\" Update brightness: python_transform=\"config['entities']['light.kitchen']['brightness'] = 50\" Remove entity: python_transform=\"del config['entities']['light.kitchen']\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead"
      • changedInput schema / properties / wait / description
        Previous value: -"Wait for scene to be queryable before returning. Default: True. Set to False for bulk operations."New value: +"Wait for scene to be queryable before returning. Set to False for bulk operations."
    • Changedha_config_set_script7 fields changed
      • changedInput schema / properties / config / description
        Previous value: -"Script configuration dictionary. Must include EITHER 'sequence' (for regular scripts) OR 'use_blueprint' (for blueprint-based scripts). Optional fields: 'alias', 'description', 'icon', 'mode', 'max', 'fields'. Mutually exclusive with python_transform."New value: +"Script configuration dictionary. Mutually exclusive with python_transform."
      • changedInput schema / properties / config_hash / description
        Previous value: -"Config hash from ha_config_get_script for optimistic locking. REQUIRED for python_transform (validates script unchanged). Optional for config updates (validates before full replacement if provided)."New value: +"Config hash from ha_config_get_script for optimistic locking. Required for python_transform; optional for config updates (validates before full replacement if provided)."
      • changedInput schema / properties / python_transform / description
        Previous value: -"Python expression to transform existing script config. Mutually exclusive with config. Requires config_hash for validation. WARNING: Expressions with infinite loops will hang the server. Examples: Simple: python_transform=\"config['sequence'][0]['data']['message'] = 'Hello'\" Pattern: python_transform=\"for step in config['sequence']: if step.get('alias') == 'My Step': step['data']['value'] = 100\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead\n\n🎯 PATTERNS:\n- Filter cards: cards = [c for c in cards if keep(c)]\n- Skip in a loop: prefer `continue` over an empty `pass` branch (clearer)\n- Conditionally include: build a new list and `.append(x)` only the\n  cards you want, instead of iterating the original and using if/pass\n  branches to drop entries\n- Modify in place when possible (single pass, fewer surprises) over\n  reconstructing the entire list"New value: +"Python expression to transform existing script config. Mutually exclusive with config. WARNING: Expressions with infinite loops will hang the server. Examples: Simple: python_transform=\"config['sequence'][0]['data']['message'] = 'Hello'\" Pattern: python_transform=\"for step in config['sequence']: if step.get('alias') == 'My Step': step['data']['value'] = 100\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead"
      • addedInput schema / properties / run
        Added value: +{
        +  "anyOf": [
        +    {
        +      "enum": [
        +        "start",
        +        "stop"
        +      ],
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Run control, used alone with script_id (no config, python_transform or take_control_of_blueprint): 'start' runs the script (script.turn_on; returns once it has started, without waiting for it to finish), 'stop' stops its running executions (script.turn_off). Stopping does not disable the script; Home Assistant has no script enable/disable."
        +}
      • changedInput schema / properties / take_control_of_blueprint / description
        Previous value: -"Convert a blueprint-backed script into an editable standalone one -- the UI's \"Take control\". Renders the blueprint with its current inputs and saves the result over the same script, which then has its own sequence and no 'use_blueprint'. Mutually exclusive with config and python_transform. Irreversible: the link to the blueprint is gone afterwards, so edit inputs instead if you only want to change a value. Does NOT free the blueprint: Home Assistant keeps counting the converted script as a user, so deleting that blueprint stays refused until the script is removed. To preview the rendering without writing anything, use ha_manage_blueprints(action=\"substitute\", domain=\"script\")."New value: +"Convert a blueprint-backed script into an editable standalone one -- the UI's \"Take control\". Renders the blueprint with its current inputs and saves the result over the same script, which keeps its script_id, alias and description and then has its own sequence and no 'use_blueprint'. Mutually exclusive with config and python_transform."
      • addedInput schema / properties / variables
        Added value: +{
        +  "anyOf": [
        +    {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "With run='start': values for the script's fields, passed to the run as its variables."
        +}
      • changedInput schema / properties / wait / description
        Previous value: -"Wait for script to be queryable before returning. Default: True. Set to False for bulk operations."New value: +"Wait for script to be queryable before returning. Set to False for bulk operations."
    • Changedha_eval_template12 fields changed
      • addedInput schema / properties / condition
        Added value: +{
        +  "anyOf": [
        +    {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Automation condition to test instead of a template, e.g. {'condition': 'numeric_state', 'entity_id': 'sensor.temp', 'above': 20}. Test several at once with {'condition': 'and', 'conditions': [...]}."
        +}
      • addedInput schema / properties / report_errors / description
        Added value: +"Template only: have Home Assistant report render errors and warnings. With false, a failed render is only written to Home Assistant's log."
      • addedInput schema / properties / strict
        Added value: +{
        +  "default": false,
        +  "description": "Template only: fail on undefined variables instead of rendering them as empty (reported in warnings when report_errors is on)",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / template / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / template / default
        Added value: +null
      • addedInput schema / properties / template / description
        Added value: +"Jinja2 template to render. Omit when using 'condition'."
      • removedInput schema / properties / template / type
        Removed value: -"string"
      • addedInput schema / properties / timeout / description
        Added value: +"Template only: maximum render time in seconds"
      • addedInput schema / properties / timeout / maximum
        Added value: +60
      • addedInput schema / properties / timeout / minimum
        Added value: +1
      • addedInput schema / properties / variables
        Added value: +{
        +  "anyOf": [
        +    {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Variables available to the template or condition, e.g. sample trigger data: {'trigger': {'to_state': {'state': 'on'}}}"
        +}
      • removedInput schema / required
        Removed value: -[
        -  "template"
        -]
    • Changedha_get_app2 fields changed
      • changedInput schema / properties / slug / description
        Previous value: -"App (add-on) slug for detailed info (e.g., '<prefix>_nodered'). Slug prefixes vary by app repository — omit to list all apps and discover the actual installed slug."New value: +"App (add-on) slug (e.g., '<prefix>_nodered'). Slug prefixes vary by app repository — omit to list all apps and discover the actual installed slug."
      • changedInput schema / properties / source / description
        Previous value: -"App (add-on) source: 'installed' (default) for currently installed apps, 'available' for apps in the store that can be installed. With source='available', 'version' is the version you would get by installing (Supervisor's version_latest) and 'version_installed' is the running one, null when the app is not installed — so compare the two, not 'version' alone, to tell whether an installed app is current."New value: +"App (add-on) source. With source='available', 'version' is the version you would get by installing (Supervisor's version_latest) and 'version_installed' is the running one, null when the app is not installed — so compare the two, not 'version' alone, to tell whether an installed app is current."
    • Changedha_get_automation_traces4 fields changed
      • changedInput schema / properties / deduplicate / description
        Previous value: -"Deduplicate variables across action steps (default: True). Set to False to include full variables at every step."New value: +"Deduplicate variables across action steps. Set to False to record the variables at every step; steps whose variables carry 'trigger', and null-valued entries, are omitted either way."
      • changedInput schema / properties / detailed / description
        Previous value: -"Include extra diagnostic data: logbook entries and context metadata (default: False). Use when standard trace lacks detail for debugging."New value: +"Include extra diagnostic data: logbook entries and context metadata."
      • changedInput schema / properties / order / description
        Previous value: -"Order traces are returned in. 'newest' (default) returns most-recent first; 'oldest' returns chronological-first."New value: +"Order traces are returned in. 'newest' returns most-recent first; 'oldest' returns chronological-first."
      • changedInput schema / properties / run_id / description
        Previous value: -"Specific trace run_id to retrieve detailed trace. Omit to list recent traces."New value: +"Specific trace run_id to retrieve detailed trace."
    • Changedha_get_camera_image3 fields changed
      • addedInput schema / properties / entity_id / description
        Added value: +"Camera entity ID (e.g., 'camera.front_door')"
      • addedInput schema / properties / height / description
        Added value: +"Height to resize the image to"
      • addedInput schema / properties / width / description
        Added value: +"Width to resize the image to"
    • Changedha_get_device4 fields changed
      • changedInput schema / properties / detail_level / description
        Previous value: -"'summary': basic device info and protocol identifiers (default for list mode). 'full': include entities and all integration details. Single device lookups always return full detail."New value: +"'summary': basic device info and protocol identifiers. 'full': in list mode also include each device's entities. Single-device lookups always return full detail, including radio metrics, node status and Matter diagnostics."
      • changedInput schema / properties / device_id / description
        Previous value: -"Device ID to retrieve details for. If omitted, lists devices."New value: +"Device ID to retrieve details for."
      • changedInput schema / properties / limit / description
        Previous value: -"Max devices to return per page in list mode (default: 50)"New value: +"Max devices to return per page in list mode"
      • changedInput schema / properties / offset / description
        Previous value: -"Number of devices to skip for pagination (default: 0)"New value: +"Number of devices to skip for pagination"
    • Changedha_get_entity2 fields changed
      • changedInput schema / properties / entity_id / description
        Previous value: -"Entity ID or list of entity IDs to retrieve (e.g., 'sensor.temperature' or ['light.living_room', 'switch.porch']). Mutually exclusive with unique_id."New value: +"Entity ID or list of entity IDs to retrieve (e.g., 'sensor.temperature' or ['light.living_room', 'switch.porch'])."
      • changedInput schema / properties / unique_id / description
        Previous value: -"Resolve a stable integration unique_id to its entity_id(s) (entity_id is mutable, unique_id is not). Mutually exclusive with entity_id. Optionally narrow with domain/platform."New value: +"Stable integration unique_id (entity_id is mutable, unique_id is not). Mutually exclusive with entity_id."
    • Changedha_get_entity_exposure2 fields changed
      • changedInput schema / properties / assistant / description
        Previous value: -"Filter by assistant: 'conversation', 'cloud.alexa', or 'cloud.google_assistant'. If not specified, returns all."New value: +"Filter by assistant: 'conversation', 'cloud.alexa', or 'cloud.google_assistant'."
      • changedInput schema / properties / entity_id / description
        Previous value: -"Entity ID to check exposure settings for. If omitted, lists all entities with exposure settings."New value: +"Entity ID to check exposure settings for."
    • Changedha_get_hacs_info3 fields changed
      • changedInput schema / properties / installed_only / description
        Previous value: -"Only return installed repositories (action='search', default: False)"New value: +"Only return installed repositories (action='search')"
      • changedInput schema / properties / max_results / description
        Previous value: -"Maximum number of results (action='search', default: 10, max: 100)"New value: +"Maximum number of results (action='search')"
      • changedInput schema / properties / offset / description
        Previous value: -"Results to skip for pagination (action='search', default: 0)"New value: +"Results to skip for pagination (action='search')"
    • Changedha_get_history8 fields changed
      • changedInput schema / properties / fields / description
        Previous value: -"Return only the specified top-level response keys to reduce response size. None = full response (default). History keys: success, source, entities, period, query_params. Statistics keys: success, source, entities, period_type, time_range, statistic_types, query_params, warnings."New value: +"Return only the specified top-level response keys to reduce response size. None = full response. History keys: success, source, entities, period, query_params. Statistics keys: success, source, entities, period_type, time_range, statistic_types, query_params, warnings."
      • changedInput schema / properties / limit / description
        Previous value: -"Max entries per entity. Default: 100, Max: 1000. For source=\"history\": state changes. For source=\"statistics\": aggregated rows. With multiple entity_ids, offset must be 0 and total rows returned can reach limit × len(entity_ids)."New value: +"Max entries per entity. Default: 100. For source=\"history\": state changes. For source=\"statistics\": aggregated rows. With multiple entity_ids, total rows returned can reach limit × len(entity_ids)."
      • changedInput schema / properties / minimal_response / description
        Previous value: -"Return only states/timestamps without attributes. Default: true. Ignored when source=\"statistics\""New value: +"Return only states/timestamps without attributes. Ignored when source=\"statistics\""
      • changedInput schema / properties / offset / description
        Previous value: -"Number of entries to skip per entity for pagination. Default: 0. Offset > 0 requires a single entity_id. Use with limit and has_more/next_offset in the response."New value: +"Number of entries to skip per entity for pagination."
      • changedInput schema / properties / order / description
        Previous value: -"Sort order for history entries. \"desc\" (default): newest first. \"asc\": oldest first (chronological, as returned by HA API). Ignored when source=\"statistics\"."New value: +"Sort order for history entries. \"desc\": newest first. \"asc\": oldest first. Ignored when source=\"statistics\"."
      • changedInput schema / properties / period / description
        Previous value: -"Aggregation period: \"5minute\", \"hour\", \"day\", \"week\", \"month\", \"year\". Default: \"day\". Ignored when source=\"history\""New value: +"Aggregation period: \"5minute\", \"hour\", \"day\", \"week\", \"month\", \"year\". Ignored when source=\"history\""
      • changedInput schema / properties / significant_changes_only / description
        Previous value: -"Filter to significant state changes only. Default: true. Ignored when source=\"statistics\""New value: +"Filter to significant state changes only. Ignored when source=\"statistics\""
      • changedInput schema / properties / source / description
        Previous value: -"Data source: \"history\" (default) for raw state changes (~10 day retention), or \"statistics\" for pre-aggregated long-term data (permanent, requires state_class)."New value: +"Data source: \"history\" for raw state changes at full resolution (~10 day retention), or \"statistics\" for pre-aggregated long-term data (permanent, requires state_class)."
    • Changedha_get_integration15 fields changed
      • changedInput schema / properties / device_id / description
        Previous value: -"Optional. When set with include_diagnostics=True, returns the device-scoped diagnostics dump for that specific device under the integration (rather than the full integration dump). Some integrations only expose config-entry-level dumps; others expose both."New value: +"With include_diagnostics=True, return the device-scoped diagnostics dump for this device instead of the full integration dump. Some integrations only expose config-entry-level dumps."
      • changedInput schema / properties / diagnostics_data_limit / description
        Previous value: -"Pagination window size for list-valued diagnostics_data_path results. When set with a list-resolved path, swaps data for a pagination envelope {path, items, offset, limit, total, has_more}. Default None returns the full resolved value. Workflow: probe with a list-valued diagnostics_data_path and diagnostics_data_limit=10 to walk a large list one page at a time (the exact path varies by integration version). Only applies when include_diagnostics=True."New value: +"Pagination window size for list-valued diagnostics_data_path results. When set with a list-resolved path, swaps data for a pagination envelope {path, items, offset, limit, total, has_more}. Default None returns the full resolved value. Only applies when include_diagnostics=True."
      • changedInput schema / properties / diagnostics_data_offset / description
        Previous value: -"Pagination start index (default 0) for list-valued diagnostics_data_path results. Ignored when diagnostics_data_path is unset, diagnostics_data_limit is unset, or the resolved value is not a list. Only applies when include_diagnostics=True."New value: +"Pagination start index for list-valued diagnostics_data_path results. Ignored when diagnostics_data_path is unset, diagnostics_data_limit is unset, or the resolved value is not a list. Only applies when include_diagnostics=True."
      • changedInput schema / properties / diagnostics_data_path / description
        Previous value: -"Optional dotted path into the diagnostics data sub-tree (e.g. '<list-valued path>' for per-device records, 'home_assistant.version' for HA core version; the exact key path varies by integration version). Walks into the post-fields payload. Resolution failures replace data with null and surface data_path_error. Use this when the interesting payload lives several levels deep — top-level diagnostics_fields can't address sub-trees on integrations where the bulk lives under one key (ZHA, MQTT, ESPHome). Only applies when include_diagnostics=True."New value: +"Dotted path into the diagnostics data sub-tree (e.g. '<list-valued path>' for per-device records, 'home_assistant.version' for HA core version; the exact key path varies by integration version). Walks into the post-fields payload. Resolution failures replace data with null and surface data_path_error. Only applies when include_diagnostics=True."
      • changedInput schema / properties / diagnostics_fields / description
        Previous value: -"Optional list of top-level keys to keep from the diagnostics data payload (e.g. ['home_assistant', 'issues']). Trims the payload before it hits the LLM context budget. Accepts a JSON list or comma-separated string. Only applies when include_diagnostics=True and the data payload is a dict. Unknown keys are silently dropped and surfaced via the omitted_fields sub-key."New value: +"Top-level keys to keep from the diagnostics data payload (e.g. ['home_assistant', 'issues']). Accepts a JSON list or comma-separated string. Only applies when include_diagnostics=True and the data payload is a dict. Unknown keys are dropped and listed under omitted_fields."
      • changedInput schema / properties / diagnostics_truncate_at_bytes / description
        Previous value: -"Optional byte cap on the serialized diagnostics payload (after diagnostics_fields and diagnostics_data_path have been applied). On hit, drops data and emits truncated=true, bytes_total, byte_cap, plus available_fields (when the capped value is a dict). Recommended starting point: 20000 bytes. Only applies when include_diagnostics=True."New value: +"Byte cap on the serialized diagnostics payload (after diagnostics_fields and diagnostics_data_path have been applied). On hit, drops data and emits truncated=true, bytes_total, byte_cap, plus available_fields (when the capped value is a dict). Recommended starting point: 20000 bytes. Only applies when include_diagnostics=True."
      • changedInput schema / properties / entry_id / description
        Previous value: -"Config entry ID to get details for. If omitted, lists all integrations."New value: +"Config entry ID to get details for."
      • changedInput schema / properties / exact_match / description
        Previous value: -"Use exact substring matching for query filter (default: True). Set to False for fuzzy matching when the query may contain typos."New value: +"Use exact substring matching for query filter. Set to False for fuzzy matching when the query may contain typos."
      • changedInput schema / properties / include_diagnostics / description
        Previous value: -"When entry_id is set, also fetch the integration's diagnostics dump — integration-defined JSON (commonly includes redacted config, device list, state snapshots; exact top-level keys vary by integration). The canonical artifact users grab via Settings → Devices & Services → [integration] → ⋯ → Download diagnostics. Use when triaging integration bugs or filing ha_report_issue for a specific integration. Payloads can be large (Hue ~290 KB, ZHA/MQTT/ESPHome several MB) — pair with diagnostics_fields or diagnostics_truncate_at_bytes to fit the LLM context budget."New value: +"When entry_id is set, also fetch the integration's diagnostics dump — integration-defined JSON (commonly includes redacted config, device list, state snapshots; exact top-level keys vary by integration), the same artifact as the UI's 'Download diagnostics'. Payloads can be large (Hue ~290 KB, ZHA/MQTT/ESPHome several MB) — pair with diagnostics_fields or diagnostics_truncate_at_bytes."
      • changedInput schema / properties / include_knx_project / description
        Previous value: -"When entry_id is a KNX config entry, also return the parsed ETS project: the full group-address table (address, name, DPT, description) under knx_project.group_addresses, plus the group-range hierarchy and project metadata. This is the parsed-project GA table that is NOT in the diagnostics dump; per-entity GA assignments are already covered by include_diagnostics (config_store / configuration_yaml). Ignored (with a warning) when the entry is not a KNX integration. The KNX integration exposes a single project, so the result is the same regardless of which KNX entry_id is used."New value: +"When entry_id is a KNX config entry, also return the parsed ETS project: the full group-address table (address, name, DPT, description) under knx_project.group_addresses, plus the group-range hierarchy and project metadata. This GA table is not in the diagnostics dump; per-entity GA assignments are (config_store / configuration_yaml). Ignored (with a warning) when the entry is not a KNX integration. KNX exposes a single project, so the result is the same for every KNX entry_id."
      • changedInput schema / properties / include_schema / description
        Previous value: -"When entry_id is set, also return the options flow schema (available fields and their types). Use before ha_config_set_helper to understand what can be updated. Only applies when supports_options=true."New value: +"When entry_id is set, also return the options flow schema (available fields and their types). Only applies when supports_options=true."
      • changedInput schema / properties / include_subentries / description
        Previous value: -"When entry_id is set, include config subentries for the integration entry. Useful for integrations that expose conversation agents, devices, or other extension points as subentries."New value: +"When entry_id is set, include config subentries for the integration entry."
      • changedInput schema / properties / limit / description
        Previous value: -"Max entries to return per page in list mode (default: 50)"New value: +"Max entries to return per page in list mode"
      • changedInput schema / properties / offset / description
        Previous value: -"Number of entries to skip for pagination (default: 0)"New value: +"Number of entries to skip for pagination"
      • changedInput schema / properties / query / description
        Previous value: -"When listing, search by domain or title. Uses exact substring matching by default; set exact_match=False for fuzzy."New value: +"When listing, search by domain or title."
    • Changedha_get_logs12 fields changed
      • addedInput schema / properties / compact / description
        Added value: +"Logbook only: strip attribute dicts to save context."
      • addedInput schema / properties / end_time / description
        Added value: +"Logbook only: end of the window (ISO datetime)."
      • addedInput schema / properties / entity_id / description
        Added value: +"Logbook only: restrict to this entity."
      • addedInput schema / properties / hours_back / description
        Added value: +"Logbook only: how many hours back to read."
      • addedInput schema / properties / level / description
        Added value: +"system / error_log only: keep only entries at exactly this level (ERROR, WARNING, INFO, DEBUG, CRITICAL); it is not a threshold."
      • addedInput schema / properties / limit / description
        Added value: +"Max entries/lines to return. Does not apply to source='error_log' with structured=True."
      • changedInput schema / properties / order / description
        Previous value: -"Sort order for time-ordered sources (logbook, system, error_log, supervisor, system_service, fault_log): 'newest' (default) returns most-recent first; 'oldest' returns chronological-first. Ignored for source='logger', and for source='error_log' with structured=True (that summary is ranked by occurrence count, not by time)."New value: +"Sort order for time-ordered sources (logbook, system, error_log, supervisor, system_service, fault_log): 'newest' returns most-recent first; 'oldest' returns chronological-first. For raw-text sources it sets the read direction of the most-recent window; fault_log orders whole crash blocks. Ignored for source='logger', and for source='error_log' with structured=True."
      • addedInput schema / properties / search / description
        Added value: +"Keyword filter on entries/lines; matches the integration domain for source='logger'. In structured error_log mode it matches the message and logger name only; on the raw path the whole line."
      • addedInput schema / properties / slug / description
        Added value: +"source='supervisor': app slug, e.g. 'core_mosquitto' (use ha_get_app() to list installed slugs). source='system_service': service name, one of supervisor, host, core, dns, audio, cli, multicast, observer — here 'supervisor' is the Supervisor service's own logs, not an app with that name."
      • addedInput schema / properties / source / description
        Added value: +"'logbook': entity state-change history. 'system': HA's structured system_log entries (errors, warnings). 'error_log': raw log text (home-assistant.log on container/pip installs; HA Core's journald stream on Supervisor-backed installs). 'supervisor': app (add-on) container logs (needs slug). 'system_service': Supervisor-managed system service logs (needs slug). 'logger': effective log level per integration (confirms ha_set_integration(log_level=...) changes took effect). 'fault_log': HA Core's faulthandler crash dump (home-assistant.log.fault), written only when HA dies from a native fatal signal, which never reaches journald or error_log; empty on a healthy install (crash_recorded=False); whole crash blocks are ordered with each block's lines kept in place; reads through the 'HA-MCP File & YAML Tools' entry."
      • changedInput schema / properties / structured / description
        Previous value: -"source='error_log' only. When True, return a deduplicated, component-grouped summary of the log (counted issues sorted by frequency) instead of raw text. Use this on busy instances where the raw log is large enough to exhaust context. Ignored for other sources."New value: +"source='error_log' only. When True, return a deduplicated, component-grouped summary of the log (counted issues sorted by frequency) instead of raw text. Ignored for other sources."
      • changedInput schema / properties / top_n / description
        Previous value: -"Max distinct issues to return when structured=True (default 20, capped at 500). Bounds the response regardless of log size."New value: +"Max distinct issues to return when structured=True (default 20, capped at 500)."
    • Changedha_get_operation_status2 fields changed
      • changedInput schema / properties / operation_id / description
        Previous value: -"Single operation ID or list of operation IDs to check. Use a single string for one operation, or a list for bulk status checks."New value: +"Single operation ID, or a list of IDs for a bulk status check."
      • addedInput schema / properties / timeout_seconds / description
        Added value: +"Seconds to wait for a pending operation to finish before returning its status. 0 returns the current status at once."
    • Changedha_get_overview7 fields changed
      • changedInput schema / properties / detail_level / description
        Previous value: -"'minimal': 10 entities/domain, top-5 states (default); 'standard': 200 entities/page, top-10 states (use offset for more); 'full': 200 entities/page + entity_id + state + full states. Use 'domains', 'limit', or max_entities_per_domain to control size"New value: +"'minimal': 10 entities/domain, top-5 states; 'standard': 200 entities/page, top-10 states (use offset for more); 'full': 200 entities/page + entity_id + state + full states."
      • changedInput schema / properties / domains / description
        Previous value: -"Filter to specific domains (e.g. 'light,sensor' or ['light','sensor']). None = all domains. Useful to avoid context window overload."New value: +"Filter to specific domains (e.g. 'light,sensor' or ['light','sensor']). None = all domains."
      • changedInput schema / properties / fields / description
        Previous value: -"Return only the specified top-level response keys to reduce response size (e.g. [\"system_info\", \"domain_stats\"]). None = full response (default). Available keys: success, system_summary, domain_stats, area_analysis, ai_insights, pagination, partial, warnings, device_types, service_availability, system_info, notification_count, notifications, repair_count, dismissed_repair_count, repairs, repairs_error, tool_discovery, settings_url, settings_url_hint, read_only_mode, read_only_mode_hint, ha_mcp_update. Note: ``settings_url`` (stdio mode), ``settings_url_hint`` (standalone HTTP/Docker mode), the ``read_only_mode`` / ``read_only_mode_hint`` pair (only while Read Only Mode is on), and ``ha_mcp_update`` (when an update check applies) are emitted regardless of ``fields=`` projection so the settings page, the active mode, and a newer ha-mcp release stay discoverable; see the tool description."New value: +"Return only the specified top-level response keys to reduce response size (e.g. [\"system_info\", \"domain_stats\"]). None = full response. Available keys: success, system_summary, domain_stats, area_analysis, ai_insights, pagination, partial, warnings, device_types, service_availability, system_info, notification_count, notifications, repair_count, dismissed_repair_count, repairs, repairs_error, tool_discovery, settings_url, settings_url_hint, read_only_mode, read_only_mode_hint, ha_mcp_update. Note: ``settings_url`` (stdio mode), ``settings_url_hint`` (standalone HTTP/Docker mode), the ``read_only_mode`` / ``read_only_mode_hint`` pair (only while Read Only Mode is on), and ``ha_mcp_update`` (when an update check applies) are emitted regardless of ``fields=`` projection."
      • changedInput schema / properties / include_dismissed_repairs / description
        Previous value: -"Include user-dismissed/ignored repairs (default: False). Matches the HA Repairs UI which hides dismissed items by default. To dismiss/ignore a repair, call ha_call_service with ws_command=\"repairs/ignore_issue\" and data={\"domain\": ..., \"issue_id\": ..., \"ignore\": true}."New value: +"Include user-dismissed/ignored repairs. Dismiss or restore one with ha_manage_updates(action='ignore_repair' / 'unignore_repair')."
      • changedInput schema / properties / include_notifications / description
        Previous value: -"Include active persistent notifications (default: True). Set False to skip."New value: +"Include active persistent notifications."
      • changedInput schema / properties / limit / description
        Previous value: -"Max total entities across all domains (default: unlimited for minimal, 200 for standard/full). Counts and states always complete. Use with offset for pagination."New value: +"Max total entities across all domains (default: unlimited for minimal, 200 for standard/full)."
      • changedInput schema / properties / offset / description
        Previous value: -"Number of entities to skip for pagination (default: 0)"New value: +"Number of entities to skip for pagination"
    • Changedha_get_skill_guide5 fields changed
      • removedInput schema / properties / file / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • changedInput schema / properties / file / default
        Previous value: -nullNew value: +"SKILL.md"
      • changedInput schema / properties / file / description
        Previous value: -"Reference file path within the skill, relative to the skill directory (e.g., 'SKILL.md' or 'references/automation-patterns.md'). Requires skill to be set."New value: +"Path of the file to read, exactly as SKILL.md links it (e.g. 'references/automation-patterns.md'). Omit to read SKILL.md."
      • addedInput schema / properties / file / type
        Added value: +"string"
      • removedInput schema / properties / skill
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Skill name from the no-args listing (e.g., 'home-assistant-best-practices')."
        -}
    • Changedha_get_state2 fields changed
      • changedInput schema / properties / attribute_keys / description
        Previous value: -"Return only the specified keys from each entity's attributes dict (e.g. [\"brightness\", \"color_temp_kelvin\"] for lights). None = full attributes (default). Unknown keys are silently dropped. Requires \"attributes\" to be present in fields= (or fields=None)."New value: +"Return only the specified keys from each entity's attributes dict (e.g. [\"brightness\", \"color_temp_kelvin\"] for lights). None = full attributes. Unknown keys are silently dropped."
      • changedInput schema / properties / fields / description
        Previous value: -"Return only the specified top-level entity record keys to reduce response size (e.g. [\"state\", \"attributes\"]). None = full entity record (default). Available keys: entity_id, state, attributes, last_changed, last_reported, last_updated, context."New value: +"Return only the specified top-level entity record keys to reduce response size (e.g. [\"state\", \"attributes\"]). None = full entity record. Available keys: entity_id, state, attributes, last_changed, last_reported, last_updated, context."
    • Changedha_get_system_health9 fields changed
      • addedInput schema / properties / config_entry_id / description
        Added value: +"Config entry ID of the integration (find via ha_get_integration). Required when include contains 'diagnostics'."
      • addedInput schema / properties / device_id / description
        Added value: +"With include='diagnostics', return the device-scoped dump for this device instead of the full integration dump. Some integrations only expose config-entry-level dumps."
      • addedInput schema / properties / diagnostics_data_limit / description
        Added value: +"Pagination window for list-valued diagnostics_data_path results; data becomes {path, items, offset, limit, total, has_more}. Only applies with include='diagnostics'."
      • addedInput schema / properties / diagnostics_data_offset / description
        Added value: +"Pagination start index for list-valued diagnostics_data_path results. Only applies with include='diagnostics'."
      • addedInput schema / properties / diagnostics_data_path / description
        Added value: +"Dotted path into the diagnostics data sub-tree (e.g. 'data.devices' for ZHA per-device records). Walks into the post-fields payload. Resolution failures replace data with null and surface data_path_error. Only applies with include='diagnostics'."
      • addedInput schema / properties / diagnostics_fields / description
        Added value: +"Top-level keys to keep from the diagnostics data payload (e.g. ['home_assistant', 'issues']). Accepts a JSON list or comma-separated string. Only applies with include='diagnostics'."
      • addedInput schema / properties / diagnostics_truncate_at_bytes / description
        Added value: +"Byte cap on the serialized diagnostics payload (post-projection / post-data_path). On hit, drops data and emits truncated=true, bytes_total, byte_cap, plus available_fields (when the capped value is a dict). Recommended starting point: 20000. Only applies with include='diagnostics'."
      • addedInput schema / properties / include / description
        Added value: +"Comma-separated extra sections: 'repairs' (Repair items, active only unless include_dismissed_repairs=True); 'zha_network' (ZHA devices with radio signal summary: name, LQI, RSSI); 'zha_network_full' (all ZHA device details; large on 100+ device networks); 'zwave_network' (Z-Wave JS status and node summary: status, security, routing); 'thread_network' (per border-router channel, extended_pan_id and border_agent_id; not per-node Thread health); 'matter_network' (Matter integration presence: config_entry_id, state, title; per-node health is in Matter node diagnostics); 'themes' (installed theme names, count, default_theme, default_dark_theme); 'diagnostics' (per-integration diagnostics dump, integration-defined JSON; REQUIRES config_entry_id; payloads can be large — pair with diagnostics_fields or diagnostics_truncate_at_bytes); 'config_check' (validate the HA configuration, the pre-restart check ha_restart runs automatically; returns {result: valid|invalid, is_valid, errors}); 'dead_entities' (orphaned/stale entity-registry entries: config_entry_orphans whose owning integration instance is gone, and stale_restored entries HA restored on startup that the loaded integration no longer provides, each with entity_id + platform for cleanup via ha_remove_entity; unknown-state entities and merely-offline devices are excluded)."
      • addedInput schema / properties / include_dismissed_repairs / description
        Added value: +"Include user-dismissed/ignored repairs. Only meaningful when 'repairs' is in include. Dismiss or restore one with ha_manage_updates(action='ignore_repair' / 'unignore_repair')."
    • Changedha_get_todo1 field changed
      • changedInput schema / properties / entity_id / description
        Previous value: -"Todo list entity ID (e.g., 'todo.shopping_list'). If omitted, lists all todo list entities."New value: +"Todo list entity ID (e.g., 'todo.shopping_list')."
    • Changedha_get_zone1 field changed
      • changedInput schema / properties / zone_id / description
        Previous value: -"Zone ID to get details for (from ha_get_zone() list). If omitted, lists all zones."New value: +"Zone ID to get details for (from ha_get_zone() list)."
    • Changedha_list_floors_areas2 fields changed
      • changedInput schema / properties / area_fields / description
        Previous value: -"Project each area record (in floors[].areas, unassigned_areas, and orphaned_areas) to only the specified keys. E.g. [\"area_id\", \"name\"] returns slim area records. None = full records (default). Unknown keys yield empty records. Available keys: area_id, name, icon, floor_id, aliases, picture, labels."New value: +"Project each area record (in floors[].areas, unassigned_areas, and orphaned_areas) to only the specified keys. E.g. [\"area_id\", \"name\"] returns slim area records. None = full records. Unknown keys yield empty records. Available keys: area_id, name, icon, floor_id, aliases, picture, labels."
      • changedInput schema / properties / fields / description
        Previous value: -"Return only the specified top-level response keys to reduce response size (e.g. [\"floors\"]). None = full response (default). Available keys: success, floor_count, area_count, unassigned_count, orphaned_count, floors, unassigned_areas, orphaned_areas, message."New value: +"Return only the specified top-level response keys to reduce response size (e.g. [\"floors\"]). None = full response. Available keys: success, floor_count, area_count, unassigned_count, orphaned_count, floors, unassigned_areas, orphaned_areas, message."
    • Changedha_list_services5 fields changed
      • changedInput schema / properties / detail_level / description
        Previous value: -"'summary': service name + description only (default). 'full': include parameter field schemas."New value: +"'summary': name, description, domain, service, and target when the service has one. 'full': additionally include parameter field schemas."
      • changedInput schema / properties / fields / description
        Previous value: -"Return only the specified top-level response keys to reduce response size (e.g. [\"services\"]). None = full response (default). Available keys: success, domains, services, total_count, count, offset, limit, has_more, next_offset, detail_level, filters_applied."New value: +"Return only the specified top-level response keys to reduce response size (e.g. [\"services\"]). None = full response. Available keys: success, domains, services, total_count, count, offset, limit, has_more, next_offset, detail_level, filters_applied."
      • changedInput schema / properties / limit / description
        Previous value: -"Max services to return per page (default: 50)"New value: +"Max services to return per page"
      • changedInput schema / properties / offset / description
        Previous value: -"Number of services to skip for pagination (default: 0)"New value: +"Number of services to skip for pagination"
      • changedInput schema / properties / service_fields / description
        Previous value: -"Project each service record to only the specified keys. E.g. [\"name\", \"description\"] returns slim service records. None = full records (default). Unknown keys yield empty records. Available keys: name, description, domain, service, fields (full mode only), target (full mode only)."New value: +"Project each service record to only the specified keys. E.g. [\"name\", \"description\"] returns slim service records. None = full records. Unknown keys yield empty records. Available keys: name, description, domain, service, target (when present), fields (full mode only)."
    • Changedha_manage_app13 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Lifecycle mode: run a Supervisor app (add-on) action. One of 'install', 'uninstall', 'start', 'stop', 'restart', 'rebuild', 'update'. 'install'/'update' require the app's repository to be registered (it appears in ha_get_app(source='available')). Store-repository mode: 'add_repository' / 'remove_repository' register or unregister a custom app store repository — these use the 'repository' param instead of 'slug'. Store-wide mode: 'check_updates' reloads the store so Supervisor re-scans its repositories, mirroring the Apps UI 'Check for updates' item — it takes neither 'slug' nor 'repository', refreshes available metadata only, and installs nothing. Use it to pick up an edited local app's config.yaml on demand instead of waiting for Supervisor's own reload (every 3h). Follow with action='update' to install a new version, or action='rebuild' for a local app whose source changed but whose version did not. Returns 'changed' and 'updates_available', each null (not empty) if the store could not be read to measure it — check 'warnings'. When ha-mcp runs as an app, it can update other apps but cannot update its own running slug; update ha-mcp from the Home Assistant Apps UI. Mutually exclusive with path / config parameters / array_patch. HA OS / Supervised only."New value: +"Lifecycle mode: run a Supervisor app (add-on) action. One of 'install', 'uninstall', 'start', 'stop', 'restart', 'rebuild', 'update'. 'install'/'update' require the app's repository to be registered (it appears in ha_get_app(source='available')); 'rebuild' is for a local app whose source changed but whose version did not. Store-repository mode: 'add_repository' / 'remove_repository' register or unregister a custom app store repository — these use the 'repository' param instead of 'slug'. Store-wide mode: 'check_updates' reloads the store so Supervisor re-scans its repositories (e.g. after editing a local app's config.yaml) — it takes neither 'slug' nor 'repository', refreshes available metadata only, and installs nothing; follow with action='update' for a newer version, or action='rebuild' for a local app whose source changed without a version change. Returns 'changed' and 'updates_available', null when the store could not be read (see 'warnings')."
      • changedInput schema / properties / debug / description
        Previous value: -"Proxy mode only. Include diagnostic info (request URL, headers sent, response headers). Default: false."New value: +"Proxy mode only. Include diagnostic info (request URL, headers sent, response headers)."
      • changedInput schema / properties / message_limit / description
        Previous value: -"Proxy mode only. WebSocket: cap on messages collected from the wire, bounded by an internal safety ceiling. None = collect up to the ceiling. Lower to save tokens on noisy streams (e.g., message_limit=50 for a quick health check)."New value: +"Proxy mode only. WebSocket: cap on messages collected from the wire, bounded by an internal safety ceiling. None = collect up to the ceiling."
      • changedInput schema / properties / message_offset / description
        Previous value: -"Proxy mode only. WebSocket: drop this many messages from the start of the collected list before returning. Useful for paginating past known-noisy headers. Default: 0."New value: +"Proxy mode only. WebSocket: drop this many messages from the start of the collected list before returning."
      • changedInput schema / properties / method / description
        Previous value: -"Proxy mode only. HTTP method: GET, POST, PUT, DELETE, PATCH. Defaults to GET."New value: +"Proxy mode only. HTTP method: GET, POST, PUT, DELETE, PATCH."
      • changedInput schema / properties / offset / description
        Previous value: -"Proxy mode only. HTTP: skip this many items in a JSON array response. Default: 0."New value: +"Proxy mode only. HTTP: skip this many items in a JSON array response."
      • changedInput schema / properties / path / description
        Previous value: -"Proxy mode: API path relative to the app (add-on) root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode; mutually exclusive with config parameters."New value: +"Proxy mode: API path relative to the app (add-on) root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode."
      • changedInput schema / properties / port / description
        Previous value: -"Proxy mode only. Connect to this port instead of the Ingress port. Use ha_get_app(slug='...') to find available ports. Some apps, including Node-RED, reject direct access unless their leave_front_door_open option is enabled and the app is restarted; related errors include an actionable, security-qualified ha_manage_app options command."New value: +"Proxy mode only. Connect to this port instead of the Ingress port. Use ha_get_app(slug='...') to find available ports. Some apps, including Node-RED, reject direct access unless their leave_front_door_open option is enabled and the app is restarted."
      • changedInput schema / properties / python_transform / description
        Previous value: -"Proxy mode only. Sandboxed Python expression that post-processes the response. Variable `response` is exposed — a list[dict | str] for WebSocket (parsed JSON or raw text), or dict/list/str for HTTP (parsed body). Supports in-place mutation (response.append(...)) or reassignment (response = [...]). Example: response = [m for m in response if 'ERROR' in str(m)]. Post-processing only — does not provide optimistic-locking write semantics."New value: +"Proxy mode only. Sandboxed Python expression that post-processes the response. Variable `response` is exposed — a list[dict | str] for WebSocket (parsed JSON or raw text), or dict/list/str for HTTP (parsed body). Supports in-place mutation (response.append(...)) or reassignment (response = [...]). Example: response = [m for m in response if 'ERROR' in str(m)]."
      • changedInput schema / properties / slug / description
        Previous value: -"App (add-on) slug (e.g., '<prefix>_nodered', '<prefix>_frigate'). Slug prefixes vary by app repository — call ha_get_app() to discover the actual installed slug. Required for every mode except the store-repository actions (action='add_repository'/'remove_repository'), which use 'repository' instead and take no slug, and the store-wide action (action='check_updates'), which takes neither."New value: +"App (add-on) slug (e.g., '<prefix>_nodered', '<prefix>_frigate'). Slug prefixes vary by app repository — call ha_get_app() to discover the actual installed slug. Required except for action='add_repository' / 'remove_repository' (which take 'repository' instead) and action='check_updates' (which takes neither)."
      • changedInput schema / properties / summarize / description
        Previous value: -"Proxy mode only. WebSocket: when True (default), collapse runs of non-signal messages (typically YAML config dumps) into short elision markers. Set to False to return the raw stream."New value: +"Proxy mode only. WebSocket: when True, collapse runs of non-signal messages (typically YAML config dumps) into short elision markers. Set to False to return the raw stream."
      • changedInput schema / properties / wait_for_close / description
        Previous value: -"Proxy mode only. WebSocket: True waits for the server to close a run-to-completion stream. False returns after the first response batch; use for one-shot command/response or bounded capture on a channel that stays open. Default: true."New value: +"Proxy mode only. WebSocket: True waits for the server to close a run-to-completion stream. False returns after the first response batch; use for one-shot command/response or bounded capture on a channel that stays open."
      • changedInput schema / properties / websocket / description
        Previous value: -"Proxy mode only. Use WebSocket instead of HTTP for an app (add-on) WebSocket API. Sends 'body' as the initial message and collects responses; command names and body schemas are app/version-specific. Default: false."New value: +"Proxy mode only. Use WebSocket instead of HTTP for an app (add-on) WebSocket API. Sends 'body' as the initial message and collects responses; command names and body schemas are app/version-specific."
    • Changedha_manage_backup2 fields changed
      • changedInput schema / properties / confirm / description
        Previous value: -"(snapshot.delete) Must be True to confirm deletion — a safety measure against accidental calls."New value: +"(snapshot.delete) Must be True to confirm deletion."
      • changedInput schema / properties / restore_database / description
        Previous value: -"(snapshot.restore) Include database in the restore. Default false (config-only)."New value: +"(snapshot.restore) Include the database in the restore; otherwise config only."
    • Changedha_manage_blueprints1 field changed
      • changedInput schema / properties / action / description
        Previous value: -"'list' installed blueprints, 'get' one blueprint's metadata/inputs/YAML, 'import' one from a URL, 'save' YAML text to a blueprint path, 'delete' an installed one, or 'substitute' to render a standalone config"New value: +"'list' installed blueprints, 'get' one blueprint's metadata/inputs/YAML, 'import' one from a URL, 'save' YAML text to a blueprint path, 'delete' an installed one, or 'substitute' to render a standalone config (the UI's \"Take control\")"
    • Changedha_manage_energy_prefs7 fields changed
      • changedInput schema / properties / config / description
        Previous value: -"Full prefs payload for mode='set'. Must contain the top-level keys you intend to replace: 'energy_sources', 'device_consumption', 'device_consumption_water'. Any top-level key present in this payload REPLACES the existing list entirely; any omitted key is preserved. Call with mode='get' first, mutate the returned config, then pass the whole object back. Ignored by convenience modes."New value: +"Full prefs payload for mode='set'. Must contain the top-level keys you intend to replace: 'energy_sources', 'device_consumption', 'device_consumption_water'. Any omitted key is preserved. Call with mode='get' first, mutate the returned config, then pass the whole object back. Ignored by convenience modes."
      • changedInput schema / properties / config_hash / description
        Previous value: -"Hash from a previous mode='get' call. REQUIRED for mode='set' unless dry_run=True. Two forms: str (full-blob lock) or dict (per-key lock, taken from the config_hash_per_key field of mode='get'). Pass the dict form as a native object, NOT a JSON-encoded string — a stringified dict is treated as a full-blob token and will report RESOURCE_LOCKED; clients that can only send strings should use the str full-blob form. See the tool docstring for fail-closed semantics. Ignored by convenience modes."New value: +"Hash from a previous mode='get' call. REQUIRED for mode='set' unless dry_run=True. Two forms: str (full-blob lock) or dict (per-key lock, taken from the config_hash_per_key field of mode='get'). Pass the dict form as a native object, NOT a JSON-encoded string — a stringified dict is treated as a full-blob token and will report RESOURCE_LOCKED; clients that can only send strings should use the str full-blob form. Ignored by convenience modes."
      • changedInput schema / properties / dry_run / description
        Previous value: -"If True, no write is performed. For mode='set': runs a local shape check on the proposed config AND calls the server's energy/validate against the CURRENT persisted state (Home Assistant's validate endpoint cannot validate an unsubmitted payload). For convenience modes: simulates the mutation against a fresh read and reports what would change without writing — but still raises RESOURCE_ALREADY_EXISTS (duplicate add_device, or duplicate add_source for solar/battery/gas/water), RESOURCE_NOT_FOUND (missing remove_device), or VALIDATION_FAILED (post-mutator shape error) when the proposed mutation is not applicable. Default False."New value: +"If True, no write is performed. For mode='set': runs a local shape check on the proposed config AND calls the server's energy/validate against the CURRENT persisted state. For convenience modes: simulates the mutation against a fresh read and reports what would change without writing — but still raises RESOURCE_ALREADY_EXISTS (duplicate add_device, or duplicate add_source for solar/battery/gas/water), RESOURCE_NOT_FOUND (missing remove_device), or VALIDATION_FAILED (post-mutator shape error) when the proposed mutation is not applicable."
      • changedInput schema / properties / included_in_stat / description
        Previous value: -"Optional 'parent' statistic for mode='add_device'. Set this to a statistic that already INCLUDES this device's consumption (e.g., a whole-home or circuit-level meter that this device feeds into). The Energy Dashboard will subtract this device's reading from the parent so the parent's contribution is not double-counted. Ignored otherwise."New value: +"'Parent' statistic for mode='add_device'. Set this to a statistic that already INCLUDES this device's consumption (e.g., a whole-home or circuit-level meter that this device feeds into). The Energy Dashboard will subtract this device's reading from the parent so the parent's contribution is not double-counted. Ignored otherwise."
      • changedInput schema / properties / mode / description
        Previous value: -"Operation mode. Primitives: 'get' reads the current prefs; 'set' writes a full prefs payload (per-top-level-key full-replace). Convenience modes: 'add_device' / 'remove_device' / 'add_source' perform a single read-modify-write atomically — no config_hash from the caller, the tool fetches it fresh internally."New value: +"Operation mode. Primitives: 'get' reads the current prefs; 'set' writes a full prefs payload."
      • changedInput schema / properties / name / description
        Previous value: -"Optional display name for mode='add_device'. Only used when adding a new device entry; ignored otherwise."New value: +"Display name for mode='add_device'; ignored otherwise."
      • changedInput schema / properties / water / description
        Previous value: -"If True, mode='add_device' / 'remove_device' targets 'device_consumption_water' instead of 'device_consumption'. Default False."New value: +"If True, mode='add_device' / 'remove_device' targets 'device_consumption_water' instead of 'device_consumption'."
    • Changedha_manage_pipeline2 fields changed
      • changedInput schema / properties / make_preferred / description
        Previous value: -"For create/update only, also set the resulting pipeline as preferred with an extra websocket call. Ignored for other actions."New value: +"For create/update only, also set the resulting pipeline as preferred. Ignored for other actions."
      • changedInput schema / properties / sentence / description
        Previous value: -"Natural-language command to run through Assist. Required when action='process'. A matched intent executes, and with the built-in agent a sentence matching a conversation trigger runs that automation."New value: +"Natural-language command to run through Assist. Required when action='process'."
    • Changedha_manage_radio2 fields changed
      • changedInput schema / properties / confirm / description
        Previous value: -"Required (True) to run destructive actions."New value: +"Required (True) to run destructive actions (e.g. remove_device, network restore, change_channel, hard_reset, remove_fabric)."
      • changedInput schema / properties / params / description
        Previous value: -"Action-specific parameters (e.g. code, pin, channel, property, value). An unknown action returns that radio's supported action list with one-line summaries."New value: +"Action-specific parameters (e.g. code, pin, channel, property, value)."
    • Changedha_manage_theme4 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Theme operation: 'list' installed themes, 'set' the backend default theme, or read/restore the screenshot engine account's own per-user theme with 'get_engine_theme' / 'set_engine_theme' (a different layer from the backend default)."New value: +"Theme operation: 'list' installed themes, 'set' the backend default theme, or read/restore the screenshot engine account's own per-user theme with 'get_engine_theme' / 'set_engine_theme'."
      • changedInput schema / properties / expected_current / description
        Previous value: -"Guard for action='set_engine_theme': the stored theme is read immediately before the write and the write is skipped if it no longer equals this. Omitting this value or passing null both mean 'expect no stored theme', enforced like any other value; the guard is always applied unless force is set. Best-effort, not atomic -- Home Assistant exposes no conditional write, so a change landing between that read and the write is not caught. Pass the expected_current value quoted in the screenshot tool's warning."New value: +"Guard for action='set_engine_theme': the stored theme is read immediately before the write and the write is skipped if it no longer equals this. Omitting this value or passing null both mean 'expect no stored theme', enforced like any other value; the guard is always applied unless force is set. Best-effort, not atomic: a change landing between that read and the write is not caught."
      • changedInput schema / properties / force / description
        Previous value: -"action='set_engine_theme' only: skip the expected_current guard and overwrite unconditionally. Leave false unless you intend to discard whatever is stored."New value: +"action='set_engine_theme' only: skip the expected_current guard and overwrite unconditionally."
      • changedInput schema / properties / value / description
        Previous value: -"Frontend user-data theme object when action='set_engine_theme', e.g. {'theme': '', 'dark': False}. An empty dict restores default/auto behavior. Take this verbatim from the warning a screenshot tool emitted."New value: +"Frontend user-data theme object when action='set_engine_theme', e.g. {'theme': '', 'dark': False}. An empty dict restores default/auto behavior."
    • Changedha_manage_updates6 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"'list' (all pending updates, default), 'get' (details/release notes for one update), 'install' (apply pending updates), 'skip' (hide the offered version), or 'clear_skipped' (re-offer a skipped version)."New value: +"'list' (all pending updates, default), 'get' (details/release notes for one update), 'install' (apply pending updates), 'skip' (hide the offered version), 'clear_skipped' (re-offer a skipped version), 'ignore_repair' (dismiss Repairs issues, as the Repairs UI's Ignore does), or 'unignore_repair' (show them again)."
      • changedInput schema / properties / backup / description
        Previous value: -"For install: create a backup before installing where the update entity supports it (apps/add-ons). Default: False."New value: +"For install: create a backup before installing where the update entity supports it (apps (add-ons))."
      • changedInput schema / properties / categories / description
        Previous value: -"For install: apply every pending update in these categories ('addons', 'hacs', 'devices', 'other'). Mirrors the HA 2026.7 'Update all' button: core/os/supervisor are excluded by design (target those individually via entity_ids) and skipped updates are never included."New value: +"For install: apply every pending update in these categories ('addons', 'hacs', 'devices', 'other'). As with the HA 'Update all' button, core/os/supervisor are excluded (target those individually via entity_ids) and skipped updates are never included."
      • changedInput schema / properties / include_release_notes / description
        Previous value: -"For get on a Core update entity: fetch multi-version release notes and breaking changes for all versions between installed and latest (default: False). Adds breaking_changes, multi_version_release_notes, and installed_integrations to the response."New value: +"For get on a Core update entity: fetch multi-version release notes and breaking changes for all versions between installed and latest. Adds breaking_changes, multi_version_release_notes, and installed_integrations to the response."
      • changedInput schema / properties / include_skipped / description
        Previous value: -"For list: include updates that have been skipped (default: False)."New value: +"For list: include updates that have been skipped."
      • addedInput schema / properties / repairs
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "For ignore_repair / unignore_repair: the Repairs issues to act on, as [{'domain': ..., 'issue_id': ...}] taken from ha_get_overview's repairs."
        +}
    • Changedha_reload_core2 fields changed
      • addedInput schema / properties / entry_id / description
        Added value: +"Reload a SINGLE config entry (one integration instance) instead of sweeping subsystems — the fast path after editing a custom component on disk. Pass it alone (leave target at 'all'); combining it with an explicit target is a validation error. Find the id via ha_get_integration."
      • addedInput schema / properties / target / description
        Added value: +"What to reload: 'all', 'automations', 'scripts', 'scenes', 'groups', 'input_booleans', 'input_numbers', 'input_texts', 'input_selects', 'input_datetimes', 'input_buttons', 'timers', 'templates', 'persons', 'zones', 'core' (customize, packages) or 'themes'."
    • Changedha_remove_entity1 field changed
      • changedInput schema / properties / entity_id / description
        Previous value: -"Entity ID, or a list of entity IDs, to remove from the entity registry (e.g., 'sensor.old_temperature'). Permanently removes the registration(s)."New value: +"Entity ID, or a list of entity IDs, to remove from the entity registry (e.g., 'sensor.old_temperature')."
    • Changedha_remove_helpers_integrations1 field changed
      • changedInput schema / properties / target / description
        Previous value: -"What to remove. One of: (a) bare helper_id for SIMPLE helpers (requires helper_type), e.g. 'my_button'; (b) full entity_id (requires helper_type), e.g. 'input_button.my_button' or 'sensor.my_meter'; (c) config entry_id for any integration (helper_type=None), e.g. value from ha_get_integration(); (d) parent config entry_id for config_subentry (requires helper_type='config_subentry' and subentry_id)."New value: +"What to remove. One of: (a) bare helper_id for SIMPLE helpers, e.g. 'my_button'; (b) full entity_id, e.g. 'input_button.my_button' or 'sensor.my_meter'; (c) config entry_id for any integration, e.g. value from ha_get_integration(); (d) parent config entry_id for a config subentry."
    • Changedha_remove_todo_item1 field changed
      • changedInput schema / properties / item / description
        Previous value: -"Item to remove - can be the item UID or the exact item summary/name"New value: +"Item to remove - can be the item UID (from ha_get_todo) or the exact item summary/name"
    • Changedha_remove_zone1 field changed
      • changedInput schema / properties / zone_id / description
        Previous value: -"Zone ID to remove (use ha_get_zone to find IDs)"New value: +"Zone ID to remove"
    • Changedha_report_issue10 fields changed
      • addedInput schema / properties / ai_model
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Your own model identity, as specific as you know it. Do not invent a version."
        +}
      • addedInput schema / properties / client_app
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "The app and version the user runs you in, as the USER states it (usually on the app's About screen or from its --version command). Never guess it."
        +}
      • addedInput schema / properties / description
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "What went wrong in markdown: steps to reproduce, expected and actual behavior. For agent_behavior: what you did and what you should have done."
        +}
      • changedInput schema / properties / fields / description
        Previous value: -"Return only the specified top-level response keys — the full response (both templates + logs + diagnostics, with log content repeated across the raw keys and templates) is very large. None = full response. Typical for a runtime bug: 'runtime_bug_template,suggested_title,runtime_bug_submit_url,duplicate_check_urls,anonymization_guide,instructions'; for agent feedback swap in agent_behavior_template and agent_behavior_submit_url. The templates already embed the relevant logs, so the raw log keys are only needed for your own analysis. Available keys: diagnostic_info, recent_logs, startup_logs, addon_logs, core_error_log, log_count, startup_log_count, formatted_report, runtime_bug_template, agent_behavior_template, anonymization_guide, suggested_title, runtime_bug_submit_url, agent_behavior_submit_url, duplicate_check_urls, missing_tool_hint, instructions."New value: +"Return only the specified top-level response keys. None = full response. Typical: 'issue_title,issue_body,issue_url,duplicate_check_urls,anonymization_guide,missing_tool_hint,known_client_issues_hint,instructions'. issue_body already embeds the relevant logs, so the raw log keys are only needed for your own analysis. Available keys: diagnostic_info, recent_logs, startup_logs, addon_logs, core_error_log, log_count, startup_log_count, formatted_report, issue_title, issue_body, issue_url, anonymization_guide, duplicate_check_urls, missing_tool_hint, known_client_issues_hint, instructions."
      • addedInput schema / properties / report_type
        Added value: +{
        +  "default": "runtime_bug",
        +  "description": "'runtime_bug' when ha-mcp errored or behaved wrongly; 'agent_behavior' when the user says you used the wrong tool or worked inefficiently.",
        +  "enum": [
        +    "runtime_bug",
        +    "agent_behavior"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / title
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "One-line summary of what broke, for the issue title."
        +}
      • changedInput schema / properties / tool_call_count / description
        Previous value: -"Number of tool calls made since the issue started. This determines how many log entries to include. Count how many ha_* tools were called from when the issue began. Default: 10. Max: 16 (limited by 200-entry log buffer: 16*4*3=192)"New value: +"Number of ha_* tool calls made since the issue started; determines how many log entries to include."
      • addedInput schema / properties / tool_calls
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "The tool call(s) that produced the problem, verbatim: name, arguments and the (shortened) response."
        +}
      • addedInput schema / properties / user_comment
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "The user's own words on what went wrong or what bothered them, verbatim. Ask the user for it; never write it yourself."
        +}
      • addedInput schema / properties / user_prompt
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "The user message that led to the problem, verbatim."
        +}
    • Changedha_restart1 field changed
      • addedInput schema / properties / confirm / description
        Added value: +"Must be True to confirm the restart."
    • Changedha_search9 fields changed
      • changedInput schema / properties / area_filter / description
        Previous value: -"Narrow entity-registry results to an area (id, name, or alias), an exact floor (id, name, or alias), or an unambiguous close-spelling floor match; a floor match expands to all areas on that floor. Does not affect configuration search."New value: +"Narrow entity-registry results to an area (id, name, or alias), an exact floor (id, name, or alias), or an unambiguous close-spelling floor match; a floor match expands to all areas on that floor."
      • changedInput schema / properties / config_time_budget / description
        Previous value: -"Per-call override for the per-id config-fetch wall-clock budget (seconds). Replaces the per-type HAMCP_*_CONFIG_TIME_BUDGET defaults for the automation, script, AND scene branches. Use when a `partial: True` response names time-budget skipping. Stateless per-call: one caller raising the budget doesn't affect others. None = use the per-type env defaults."New value: +"Per-call override for the per-id config-fetch wall-clock budget (seconds). Replaces the per-type HAMCP_*_CONFIG_TIME_BUDGET defaults for the automation, script, AND scene branches. Use when a `partial: True` response names time-budget skipping. None = use the per-type env defaults."
      • changedInput schema / properties / domain_filter / description
        Previous value: -"Narrow entity-registry results to a single domain (e.g. 'light', 'sensor'). Does not affect configuration search."New value: +"Narrow entity-registry results to a single domain (e.g. 'light', 'sensor')."
      • changedInput schema / properties / exact_match / description
        Previous value: -"Exact substring matching (default). Set False for fuzzy matching when the query may have typos."New value: +"Exact substring matching. Set False for fuzzy matching when the query may have typos."
      • changedInput schema / properties / include_config / description
        Previous value: -"Include full configuration bodies in body-search results. Default: False (summary only)."New value: +"Include full configuration bodies in body-search results. Otherwise summaries only."
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum results per surface (entities, configs). Default: 10."New value: +"Maximum results per surface (entities, configs)."
      • changedInput schema / properties / query / description
        Previous value: -"What to search for (entity name fragment, free-text config term, entity_id). Searches BOTH the entity registry (entity_ids, friendly names, areas) AND configuration bodies (automation triggers/actions, script sequences, scene contents, helper bodies, dashboard cards) in one call. Use this for any find-something-in-HA question — entity OR config. Pass the exact entity_id, not a name fragment, when checking what a rename or delete would break: that form reports automations, scripts and scenes referencing it even when their configuration could not be read. Omit `query` to enumerate by `domain_filter`, `area_filter`, and/or `state_filter` alone (registry-listing mode); configuration-body search is skipped in that mode because there is no term to match against."New value: +"What to search for (entity name fragment, free-text config term, entity_id). Pass the exact entity_id, not a name fragment, when checking what a rename or delete would break: that form reports automations, scripts and scenes referencing it even when their configuration could not be read. Omit `query` to enumerate by `domain_filter`, `area_filter`, and/or `state_filter` alone (registry-listing mode); configuration-body search is skipped in that mode because there is no term to match against."
      • changedInput schema / properties / result_fields / description
        Previous value: -"Project each entity-registry record to only the specified keys (e.g. [\"entity_id\", \"state\"]). None = full records. Base keys: entity_id, friendly_name, domain, state, score, match_type. Opt-in enrichment/membership keys (computed on request): area, floor, labels, aliases, is_group, member_entity_ids. Membership is recognized only when HA explicitly exposes a valid group_entities or legacy entity_id collection; member IDs are sorted, direct (not recursively expanded), and omitted if visibility/include_hidden excludes a member. is_group remains true when member IDs are withheld; requesting member_entity_ids also retains is_group. An unknown key is rejected."New value: +"Project each entity-registry record to only the specified keys (e.g. [\"entity_id\", \"state\"]). None = full records. Base keys: entity_id, friendly_name, domain, state, score, match_type. Opt-in enrichment/membership keys (computed on request): area, floor, labels, aliases, is_group, member_entity_ids. Membership is recognized only when HA explicitly exposes a valid group_entities or legacy entity_id collection; member IDs are sorted, direct (not recursively expanded), and omitted if visibility/include_hidden excludes a member. Requesting member_entity_ids also retains is_group. An unknown key is rejected."
      • changedInput schema / properties / search_types / description
        Previous value: -"Configuration types to include in body search: 'automation', 'script', 'scene', 'helper', 'dashboard'. Default = automation+script+scene+helper. Pass as list or JSON-array string."New value: +"Configuration types to include in body search: 'automation', 'script', 'scene', 'helper', 'dashboard'. Explicitly providing this selects configuration-only search and skips entities. Omit it for entity discovery. Default = automation+script+scene+helper."
    • Changedha_set_area_or_floor5 fields changed
      • changedInput schema / properties / floor_id / description
        Previous value: -"Floor assignment when kind='area' (use empty string to clear). Only valid when kind='area'."New value: +"Floor assignment when kind='area' (use empty string to clear)."
      • changedInput schema / properties / id / description
        Previous value: -"Existing area_id or floor_id to update (omit to create a new entry; use ha_list_floors_areas to find IDs)"New value: +"Existing area_id or floor_id to update (use ha_list_floors_areas to find IDs)"
      • addedInput schema / properties / labels
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Label IDs when kind='area' (replaces the area's label set; empty list to clear). Omit to leave labels unchanged. Floors have no labels."
        +}
      • changedInput schema / properties / level / description
        Previous value: -"Numeric level when kind='floor' (0=ground, 1=first, -1=basement). Only valid when kind='floor'."New value: +"Numeric level when kind='floor' (0=ground, 1=first, -1=basement)."
      • changedInput schema / properties / picture / description
        Previous value: -"Picture URL when kind='area' (empty string to remove). Only valid when kind='area'."New value: +"Picture URL when kind='area' (empty string to remove)."
    • Changedha_set_device1 field changed
      • changedInput schema / properties / disabled_by / description
        Previous value: -"Set to 'user' to disable, or None/empty string to enable"New value: +"Set to 'user' to disable the device, or '' (empty string) to enable it. Omit to leave the disabled state unchanged."
    • Changedha_set_entity16 fields changed
      • changedInput schema / properties / aliases / anyOf
        Previous value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "items": {
        -      "type": "string"
        -    },
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "items": {
        +      "anyOf": [
        +        {
        +          "type": "string"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ]
        +    },
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / aliases / description
        Previous value: -"List of voice assistant aliases for the entity (replaces existing aliases). Single entity only."New value: +"List of voice assistant aliases for the entity (replaces existing aliases). A null entry is the entity's own name (HA's 'use entity name' switch); it is kept automatically unless your list already contains null. To turn that switch off or on, use use_entity_name_alias."
      • changedInput schema / properties / area_id / description
        Previous value: -"Area/room ID to assign the entity to. Use empty string '' to unassign from current area. Single entity only."New value: +"Area/room ID to assign the entity to. Use empty string '' to unassign from current area."
      • changedInput schema / properties / categories / description
        Previous value: -"Category assignment as a dict mapping scope to category_id. Example: {\"automation\": \"category_id_here\"}. Use null value to clear: {\"automation\": null}. Single entity only."New value: +"Category assignment as a dict mapping scope to category_id. Example: {\"automation\": \"category_id_here\"}. Use null value to clear: {\"automation\": null}."
      • changedInput schema / properties / device_class / description
        Previous value: -"Override the entity's display device class — what the HA UI's 'Show As' dropdown writes. Use empty string '' to clear the override and fall back to the integration default. None (the default) means 'no change' — pass an explicit '' to clear. Single entity only. Examples: 'window', 'door', 'motion' for binary_sensor; 'temperature', 'humidity' for sensor."New value: +"Override the entity's display device class — what the HA UI's 'Show As' dropdown writes. Use empty string '' to clear the override and fall back to the integration default. Examples: 'window', 'door', 'motion' for binary_sensor; 'temperature', 'humidity' for sensor."
      • changedInput schema / properties / enabled / description
        Previous value: -"True to enable the entity, False to disable it. Single entity only. WARNING: Setting enabled=False is a registry-level disable — it completely removes the entity from the state machine and hides it from the UI. A reload or restart is required to restore it after re-enabling. NOT allowed for automation or script entities — use automation.turn_off / script.turn_off via ha_call_service() instead."New value: +"True to enable the entity, False to disable it. WARNING: Setting enabled=False is a registry-level disable — it completely removes the entity from the state machine and hides it from the UI. A reload or restart is required to restore it after re-enabling."
      • changedInput schema / properties / entity_id / description
        Previous value: -"Entity ID or list of entity IDs to update. Bulk operations (list) only support labels, expose_to, and categories parameters."New value: +"Entity ID or list of entity IDs to update."
      • changedInput schema / properties / expose_to / description
        Previous value: -"Control voice assistant exposure. Pass a dict mapping assistant IDs to booleans. Valid assistants: 'conversation' (Assist), 'cloud.alexa', 'cloud.google_assistant'. Example: {\"conversation\": true, \"cloud.alexa\": false}. Supports bulk operations."New value: +"Control voice assistant exposure. Pass a dict mapping assistant IDs to booleans. Valid assistants: 'conversation' (Assist), 'cloud.alexa', 'cloud.google_assistant'. Example: {\"conversation\": true, \"cloud.alexa\": false}."
      • changedInput schema / properties / hidden / description
        Previous value: -"True to hide the entity from UI, False to show it. Single entity only."New value: +"True to hide the entity from UI, False to show it."
      • changedInput schema / properties / icon / description
        Previous value: -"Icon for the entity (e.g., 'mdi:thermometer'). Use empty string '' to remove custom icon. Single entity only."New value: +"Icon for the entity (e.g., 'mdi:thermometer'). Use empty string '' to remove custom icon."
      • changedInput schema / properties / labels / description
        Previous value: -"List of label IDs for the entity. Behavior depends on label_operation parameter. Supports bulk operations."New value: +"List of label IDs for the entity. Behavior depends on label_operation parameter. Use [] with label_operation='set' to clear."
      • changedInput schema / properties / name / description
        Previous value: -"Display name for the entity. Use empty string '' to remove custom name and revert to default. Single entity only."New value: +"Display name for the entity. Use empty string '' to remove custom name and revert to default."
      • changedInput schema / properties / new_device_name / description
        Previous value: -"New display name for the associated device. If provided, both entity and device are updated in one operation. Single entity only."New value: +"New display name for the associated device. If provided, both entity and device are updated in one operation."
      • changedInput schema / properties / new_entity_id / description
        Previous value: -"New entity ID to rename to (e.g., 'light.new_name'). Domain must match the original. Single entity only."New value: +"New entity ID to rename to (e.g., 'light.new_name'). Domain must match the original."
      • changedInput schema / properties / options / description
        Previous value: -"Per-domain entity registry options (e.g. sensor 'display_precision', weather 'forecast_type'). Pass a dict mapping domain to a sub-dict, e.g. {\"sensor\": {\"display_precision\": 2}}. Multiple domains are sent as separate registry updates. For 'Show As' use the dedicated `device_class` parameter — that is what the HA UI Show As dropdown writes. Voice-assistant exposure is stored under `options.<assistant>.should_expose` but must be managed via the dedicated `expose_to` parameter, not this options dict. Single entity only."New value: +"Per-domain entity registry options (e.g. sensor 'display_precision', weather 'forecast_type'). Pass a dict mapping domain to a sub-dict, e.g. {\"sensor\": {\"display_precision\": 2}}. Multiple domains are sent as separate registry updates. For 'Show As' use device_class; for voice-assistant exposure use expose_to, not options.<assistant>.should_expose."
      • addedInput schema / properties / use_entity_name_alias
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "boolean"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "HA's 'use entity name' voice-alias switch. True keeps the entity's own name answering in Assist, False turns it off so only the aliases match. Omit to leave it as is. Works with or without aliases."
        +}
    • Changedha_set_integration6 fields changed
      • changedInput schema / properties / config / description
        Previous value: -"Flow form data. With 'domain': input for the new integration's config flow. With 'entry_id' alone: input for the entry's options flow (updates its options). Updating an existing entry — options or reconfigure — is a patch: a field you omit keeps its current value, and a field set to null is cleared where the integration's schema allows that field to be empty. Multi-step flows consume keys per step. A field two steps declare gets your one value both times; pass step_values={'<step_id>': {'<field>': <value>}} to give a step its own value, or to leave the field out of that step; a LIST of those objects supplies one per encounter when the flow presents a step more than once. Menu steps take 'next_step_id' — a string, or a list of successive selections for flows that present more than one menu (e.g. a menu revisited after each branch, ending in a finish option). The step's data_schema is returned on validation errors so field names can be corrected."New value: +"Flow form data. Updating an existing entry — options or reconfigure — is a patch: a field you omit keeps its current value, and a field set to null is cleared where the integration's schema allows that field to be empty. Multi-step flows consume keys per step. A field two steps declare gets your one value both times; pass step_values={'<step_id>': {'<field>': <value>}} to give a step its own value, or to leave the field out of that step; a LIST of those objects supplies one per encounter when the flow presents a step more than once. Menu steps take 'next_step_id' — a string, or a list of successive selections for flows that present more than one menu (e.g. a menu revisited after each branch, ending in a finish option). The step's data_schema is returned on validation errors so field names can be corrected."
      • changedInput schema / properties / confirm_token / description
        Previous value: -"Requires reconfigure=True. A token from a reconfigure preflight; applies the change. Any token still matching the entry's current state and the same requested config is accepted, so a token stays valid while nothing moves."New value: +"Requires reconfigure=True. Any token still matching the entry's current state and the same requested config is accepted, so a token stays valid while nothing moves."
      • changedInput schema / properties / domain / description
        Previous value: -"Integration domain to add (e.g. 'workday', 'local_calendar') — starts and drives that domain's config flow. Pass the flow's form fields in 'config'."New value: +"Integration domain to add (e.g. 'workday', 'local_calendar')."
      • changedInput schema / properties / enabled / description
        Previous value: -"True to enable, False to disable the entry. Requires entry_id; mutually exclusive with 'domain' and 'config'."New value: +"True to enable, False to disable the entry. Mutually exclusive with 'domain' and 'config'."
      • changedInput schema / properties / entry_id / description
        Previous value: -"Config entry ID of an existing integration (enable/disable and options-update modes). Omit when adding via 'domain'."New value: +"Config entry ID of an existing integration."
      • addedInput schema / properties / log_level
        Added value: +{
        +  "anyOf": [
        +    {
        +      "enum": [
        +        "DEBUG",
        +        "INFO",
        +        "WARNING",
        +        "ERROR",
        +        "CRITICAL",
        +        "DEFAULT"
        +      ],
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Set the integration's log level, like the integration page's Enable/Disable debug logging: pass the integration with 'domain' (no config flow runs) or 'entry_id', and nothing else. Lasts through the next Home Assistant restart. DEFAULT clears the override: the integration then logs at the inherited default level, and a level set for it in configuration.yaml returns only after a restart."
        +}
    • Changedha_set_todo_item2 fields changed
      • changedInput schema / properties / item / description
        Previous value: -"Existing item to update - can be the item UID or the exact item summary/name. When provided, operates in update mode. When omitted, creates a new item."New value: +"Existing item to update - can be the item UID or the exact item summary/name."
      • changedInput schema / properties / summary / description
        Previous value: -"Item text/name. Required when creating a new item. Ignored in update mode — use 'rename' to change the item name."New value: +"Item text/name. Ignored in update mode — use 'rename' to change the item name."
    • Changedha_set_zone4 fields changed
      • changedInput schema / properties / latitude / description
        Previous value: -"Latitude coordinate of the zone center (required for create)"New value: +"Latitude coordinate of the zone center"
      • changedInput schema / properties / longitude / description
        Previous value: -"Longitude coordinate of the zone center (required for create)"New value: +"Longitude coordinate of the zone center"
      • changedInput schema / properties / name / description
        Previous value: -"Display name for the zone (required for create)"New value: +"Display name for the zone"
      • changedInput schema / properties / zone_id / description
        Previous value: -"Zone ID to update (omit to create new zone, use ha_get_zone to find IDs)"New value: +"Zone ID to update (use ha_get_zone to find IDs)"
  2. 12 tool updatesv8.5.0
    • Changedha_config_get_dashboard1 field changed
      • changedInput schema / properties / force_reload / description
        Previous value: -"Force reload from storage (bypass cache). Not applicable in search mode (search always uses force=True for fresh results)."New value: +"Force reload from storage (bypass cache). Not applicable in search mode, which always reads fresh config."
    • Changedha_config_set_automation3 fields changed
      • changedInput schema / properties / config_hash / description
        Previous value: -"Config hash from ha_config_get_automation for optimistic locking. REQUIRED for python_transform (validates automation unchanged). Optional for config updates (validates before full replacement if provided)."New value: +"Config hash from ha_config_get_automation for optimistic locking. REQUIRED for python_transform (validates automation unchanged). Required when a config update changes an existing automation's alias. Otherwise optional for config updates (validates before full replacement if provided)."
      • changedInput schema / properties / identifier / description
        Previous value: -"Automation entity_id or unique_id for updates. Required for python_transform. Omit to create new automation with generated unique_id."New value: +"Target automation entity_id or HA config 'id' (unique_id). Omit for creation with a generated ID. Values such as 'new' are literal IDs, not placeholders. Required for python_transform."
      • addedInput schema / properties / take_control_of_blueprint
        Added value: +{
        +  "default": false,
        +  "description": "Convert a blueprint-backed automation into an editable standalone one -- the UI's \"Take control\". Renders the blueprint with its current inputs and saves the result over the same automation, which then has its own triggers/conditions/actions and no 'use_blueprint'. Requires identifier; mutually exclusive with config and python_transform. Irreversible: the link to the blueprint is gone afterwards, so edit inputs instead if you only want to change a value. Does NOT free the blueprint: Home Assistant keeps counting the converted automation as a user, so deleting that blueprint stays refused until the automation is removed. To preview the rendering without writing anything, use ha_manage_blueprints(action=\"substitute\").",
        +  "type": "boolean"
        +}
    • Changedha_config_set_dashboard4 fields changed
      • changedInput schema / properties / config / description
        Previous value: -"Dashboard configuration with views and cards. Omit or set to None to create dashboard without initial config. Mutually exclusive with python_transform."New value: +"Dashboard configuration with views and cards. Omit or set to None to create dashboard without initial config. Mutually exclusive with python_transform and patch."
      • changedInput schema / properties / config_hash / description
        Previous value: -"Config hash from ha_config_get_dashboard for optimistic locking. REQUIRED for python_transform (validates dashboard unchanged). Optional for config (validates before full replacement if provided)."New value: +"Config hash from ha_config_get_dashboard for optimistic locking. REQUIRED for python_transform and patch (validates dashboard unchanged). Optional for config (validates before full replacement if provided)."
      • addedInput schema / properties / patch
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "additionalProperties": true,
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Structured dashboard edits: up to 100 JSON Patch add, remove, replace or test operations using RFC 6901 paths. Use /- to append to an array; escape ~ as ~0 and / as ~1 in keys. Requires config_hash. Mutually exclusive with config and python_transform. Update title/icon/require_admin/show_in_sidebar in a separate call. Strings in value are preserved literally."
        +}
      • changedInput schema / properties / python_transform / description
        Previous value: -"Python expression to transform existing dashboard config. Mutually exclusive with config. Requires config_hash for validation. See PYTHON TRANSFORM SECURITY below for allowed operations. Examples: Simple: python_transform=\"config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'\" Pattern: python_transform=\"for card in config['views'][0]['cards']: if 'light' in card.get('entity', ''): card['icon'] = 'mdi:lightbulb'\" Multi-op: python_transform=\"config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'; del config['views'][0]['cards'][2]\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead\n\n🎯 PATTERNS:\n- Filter cards: cards = [c for c in cards if keep(c)]\n- Skip in a loop: prefer `continue` over an empty `pass` branch (clearer)\n- Conditionally include: build a new list and `.append(x)` only the\n  cards you want, instead of iterating the original and using if/pass\n  branches to drop entries\n- Modify in place when possible (single pass, fewer surprises) over\n  reconstructing the entire list"New value: +"Python expression to transform existing dashboard config. Mutually exclusive with config and patch. Requires config_hash for validation. See PYTHON TRANSFORM SECURITY below for allowed operations. Examples: Simple: python_transform=\"config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'\" Pattern: python_transform=\"for card in config['views'][0]['cards']: if 'light' in card.get('entity', ''): card['icon'] = 'mdi:lightbulb'\" Multi-op: python_transform=\"config['views'][0]['cards'][0]['icon'] = 'mdi:lamp'; del config['views'][0]['cards'][2]\" \n\nPYTHON TRANSFORM SECURITY:\n\n✅ ALLOWED:\n- Dictionary/list access: config['views'][0]['cards'][1]\n- Slicing: config['views'][0]['cards'][1:3]\n- Assignment: config['key'] = 'value'\n- Deletion: del config['key'] or config.pop('key')\n- List methods: append, insert, pop, remove, clear, extend\n- Dict methods: update, get, setdefault, keys, values, items\n- Loops: for, if/else, pass, break, continue\n- Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...)\n- Ternary: x if condition else y\n- Iterable unpacking (* in calls/literals): f(*xs), [*xs, y]\n- Dict unpacking (**) in calls and dict literals: {**d, 'k': v}\n- Keyword arguments: func(key=value)\n- Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score'])\n- String methods: startswith, endswith, lower, upper, strip, split, join, replace\n- Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed,\n  min, max, sum, abs, any, all, round, str, int, float, bool, list, dict,\n  tuple, set\n\n❌ FORBIDDEN:\n- Imports: import, from, __import__\n- File operations: open, read, write\n- Dunder access: __class__, __bases__, __subclasses__\n- Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr\n- Function definitions: def, class\n- Exception handling: try/except (validate with isinstance/in/.get() instead)\n- While loops: use bounded for loops or comprehensions instead\n\n🎯 PATTERNS:\n- Filter cards: cards = [c for c in cards if keep(c)]\n- Skip in a loop: prefer `continue` over an empty `pass` branch (clearer)\n- Conditionally include: build a new list and `.append(x)` only the\n  cards you want, instead of iterating the original and using if/pass\n  branches to drop entries\n- Modify in place when possible (single pass, fewer surprises) over\n  reconstructing the entire list"
    • Changedha_config_set_script1 field changed
      • addedInput schema / properties / take_control_of_blueprint
        Added value: +{
        +  "default": false,
        +  "description": "Convert a blueprint-backed script into an editable standalone one -- the UI's \"Take control\". Renders the blueprint with its current inputs and saves the result over the same script, which then has its own sequence and no 'use_blueprint'. Mutually exclusive with config and python_transform. Irreversible: the link to the blueprint is gone afterwards, so edit inputs instead if you only want to change a value. Does NOT free the blueprint: Home Assistant keeps counting the converted script as a user, so deleting that blueprint stays refused until the script is removed. To preview the rendering without writing anything, use ha_manage_blueprints(action=\"substitute\", domain=\"script\").",
        +  "type": "boolean"
        +}
    • Changedha_get_app1 field changed
      • changedInput schema / properties / source / description
        Previous value: -"App (add-on) source: 'installed' (default) for currently installed apps, 'available' for apps in the store that can be installed."New value: +"App (add-on) source: 'installed' (default) for currently installed apps, 'available' for apps in the store that can be installed. With source='available', 'version' is the version you would get by installing (Supervisor's version_latest) and 'version_installed' is the running one, null when the app is not installed — so compare the two, not 'version' alone, to tell whether an installed app is current."
    • Removedha_get_blueprint
    • Changedha_get_logs3 fields changed
      • changedInput schema / properties / offset / description
        Previous value: -"Page deeper into source='logbook' and source='error_log' (ignored for other sources). On error_log it counts raw log lines back from the newest entry; pass the response's 'next_offset' to continue while 'has_more' is true."New value: +"Page deeper into source='logbook', 'error_log' and 'fault_log' (ignored for other sources). On error_log it counts raw log lines back from the newest entry; on fault_log it counts lines from the start of the assembled crash text. Pass the response's 'next_offset' to continue while 'has_more' is true."
      • changedInput schema / properties / order / description
        Previous value: -"Sort order for time-ordered sources (logbook, system, error_log, supervisor, system_service): 'newest' (default) returns most-recent first; 'oldest' returns chronological-first. Ignored for source='logger', and for source='error_log' with structured=True (that summary is ranked by occurrence count, not by time)."New value: +"Sort order for time-ordered sources (logbook, system, error_log, supervisor, system_service, fault_log): 'newest' (default) returns most-recent first; 'oldest' returns chronological-first. Ignored for source='logger', and for source='error_log' with structured=True (that summary is ranked by occurrence count, not by time)."
      • changedInput schema / properties / source / enum
        Previous value: -[
        -  "logbook",
        -  "system",
        -  "error_log",
        -  "supervisor",
        -  "system_service",
        -  "logger"
        -]New value: +[
        +  "logbook",
        +  "system",
        +  "error_log",
        +  "supervisor",
        +  "system_service",
        +  "logger",
        +  "fault_log"
        +]
    • Changedha_get_overview1 field changed
      • changedInput schema / properties / fields / description
        Previous value: -"Return only the specified top-level response keys to reduce response size (e.g. [\"system_info\", \"domains\"]). None = full response (default). Available keys: success, system_summary, domain_stats, area_analysis, ai_insights, pagination, partial, warnings, device_types, service_availability, system_info, notification_count, notifications, repair_count, dismissed_repair_count, repairs, repairs_error, tool_discovery, settings_url, settings_url_hint, read_only_mode, read_only_mode_hint, ha_mcp_update. Note: ``settings_url`` (stdio mode), ``settings_url_hint`` (standalone HTTP/Docker mode), the ``read_only_mode`` / ``read_only_mode_hint`` pair (only while Read Only Mode is on), and ``ha_mcp_update`` (when an update check applies) are emitted regardless of ``fields=`` projection so the settings page, the active mode, and a newer ha-mcp release stay discoverable; see the tool description."New value: +"Return only the specified top-level response keys to reduce response size (e.g. [\"system_info\", \"domain_stats\"]). None = full response (default). Available keys: success, system_summary, domain_stats, area_analysis, ai_insights, pagination, partial, warnings, device_types, service_availability, system_info, notification_count, notifications, repair_count, dismissed_repair_count, repairs, repairs_error, tool_discovery, settings_url, settings_url_hint, read_only_mode, read_only_mode_hint, ha_mcp_update. Note: ``settings_url`` (stdio mode), ``settings_url_hint`` (standalone HTTP/Docker mode), the ``read_only_mode`` / ``read_only_mode_hint`` pair (only while Read Only Mode is on), and ``ha_mcp_update`` (when an update check applies) are emitted regardless of ``fields=`` projection so the settings page, the active mode, and a newer ha-mcp release stay discoverable; see the tool description."
    • Removedha_import_blueprint
    • Changedha_manage_app2 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Lifecycle mode: run a Supervisor app (add-on) action. One of 'install', 'uninstall', 'start', 'stop', 'restart', 'rebuild', 'update'. 'install'/'update' require the app's repository to be registered (it appears in ha_get_app(source='available')). Store-repository mode: 'add_repository' / 'remove_repository' register or unregister a custom app store repository — these use the 'repository' param instead of 'slug'. When ha-mcp runs as an app, it can update other apps but cannot update its own running slug; update ha-mcp from the Home Assistant Apps UI. Mutually exclusive with path / config parameters / array_patch. HA OS / Supervised only."New value: +"Lifecycle mode: run a Supervisor app (add-on) action. One of 'install', 'uninstall', 'start', 'stop', 'restart', 'rebuild', 'update'. 'install'/'update' require the app's repository to be registered (it appears in ha_get_app(source='available')). Store-repository mode: 'add_repository' / 'remove_repository' register or unregister a custom app store repository — these use the 'repository' param instead of 'slug'. Store-wide mode: 'check_updates' reloads the store so Supervisor re-scans its repositories, mirroring the Apps UI 'Check for updates' item — it takes neither 'slug' nor 'repository', refreshes available metadata only, and installs nothing. Use it to pick up an edited local app's config.yaml on demand instead of waiting for Supervisor's own reload (every 3h). Follow with action='update' to install a new version, or action='rebuild' for a local app whose source changed but whose version did not. Returns 'changed' and 'updates_available', each null (not empty) if the store could not be read to measure it — check 'warnings'. When ha-mcp runs as an app, it can update other apps but cannot update its own running slug; update ha-mcp from the Home Assistant Apps UI. Mutually exclusive with path / config parameters / array_patch. HA OS / Supervised only."
      • changedInput schema / properties / slug / description
        Previous value: -"App (add-on) slug (e.g., '<prefix>_nodered', '<prefix>_frigate'). Slug prefixes vary by app repository — call ha_get_app() to discover the actual installed slug. Required for every mode except the store-repository actions (action='add_repository'/'remove_repository'), which use 'repository' instead and take no slug."New value: +"App (add-on) slug (e.g., '<prefix>_nodered', '<prefix>_frigate'). Slug prefixes vary by app repository — call ha_get_app() to discover the actual installed slug. Required for every mode except the store-repository actions (action='add_repository'/'remove_repository'), which use 'repository' instead and take no slug, and the store-wide action (action='check_updates'), which takes neither."
    • Changedha_manage_backup1 field changed
      • changedInput schema / properties / backup_name / description
        Previous value: -"(edits.view / edits.restore / edits.delete) Auto-backup filename (format '<domain>.<entity_id>.<timestamp>.yaml'). Not a tarball ID."New value: +"(edits.view / edits.restore / edits.delete) Auto-backup filename (format '<domain>.<entity_id>.<timestamp>[_NN].yaml'). Not a tarball ID."
    • Addedha_manage_blueprints
  3. 1 tool updatev8.4.3
    • Changedha_search1 field changed
      • changedInput schema / properties / config_time_budget / anyOf
        Previous value: -[
        -  {
        -    "exclusiveMinimum": 0,
        -    "maximum": 300,
        -    "type": "number"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "maximum": 300,
        +    "minimum": 0.001,
        +    "type": "number"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
  4. 14 tool updatesv8.4.1
    • Changedha_bulk_control13 fields changed
      • addedInput schema / properties / action
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "One device action applied to every resolved leaf."
        +}
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": false,
        +  "type": "boolean"
        +}
      • addedInput schema / properties / operations / default
        Added value: +null
      • addedInput schema / properties / operations / description
        Added value: +"Explicit entity operations. Use this or selector, never both. Each item requires exact entity_id and action. Use action='off', not service='turn_off'."
      • changedInput schema / properties / operations / items / additionalProperties
        Previous value: -trueNew value: +false
      • addedInput schema / properties / operations / items / description
        Added value: +"One entity action in a ha_bulk_control request."
      • addedInput schema / properties / operations / items / properties
        Added value: +{
        +  "action": {
        +    "description": "Device action such as 'on', 'off', or 'toggle'. For lights, use 'off' instead of the ha_call_service form 'turn_off'.",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  "entity_id": {
        +    "description": "Exact Home Assistant entity ID, e.g. 'light.kitchen'.",
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  "parameters": {
        +    "additionalProperties": true,
        +    "description": "Optional action parameters, e.g. {'brightness_pct': 30} when action='on'. Each domain has a fixed allowlist of supported keys; keys outside it are ignored rather than rejected. Use ha_call_service for parameters this tool does not carry.",
        +    "type": "object"
        +  },
        +  "timeout_seconds": {
        +    "description": "Optional confirmation timeout. On the component path, all operations share the maximum requested wait (default 10s, capped at 60s); 0 disables confirmation waiting.",
        +    "minimum": 0,
        +    "type": "number"
        +  },
        +  "validate_first": {
        +    "description": "Report an ENTITY_NOT_FOUND failure when the target entity does not exist; default true. On the component batch path this is detected from the captured pre-state rather than by preventing dispatch. The action is always validated.",
        +    "type": "boolean"
        +  }
        +}
      • addedInput schema / properties / operations / items / required
        Added value: +[
        +  "entity_id",
        +  "action"
        +]
      • addedInput schema / properties / parameters
        Added value: +{
        +  "anyOf": [
        +    {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Optional action parameters for selector mode."
        +}
      • addedInput schema / properties / selector
        Added value: +{
        +  "anyOf": [
        +    {
        +      "additionalProperties": false,
        +      "description": "Exact structural scope for one deterministic bulk action.\n\nLives here (not in ``tools_service.py``) so ``_SELECTOR_KEYS`` below can\nderive from this single field set instead of duplicating it as an\nindependent literal -- the import direction (``tools_service`` already\nimports from this module) makes that safe without a cycle.",
        +      "properties": {
        +        "area_ids": {
        +          "description": "Exact Home Assistant area IDs to include.",
        +          "items": {
        +            "type": "string"
        +          },
        +          "type": "array"
        +        },
        +        "domain": {
        +          "description": "Exact Home Assistant domain, e.g. 'light'.",
        +          "type": "string"
        +        },
        +        "exclude_entity_ids": {
        +          "description": "Exact entity or aggregate IDs to exclude after recursive membership expansion.",
        +          "items": {
        +            "type": "string"
        +          },
        +          "type": "array"
        +        },
        +        "floor_ids": {
        +          "description": "Exact Home Assistant floor IDs to include.",
        +          "items": {
        +            "type": "string"
        +          },
        +          "type": "array"
        +        }
        +      },
        +      "required": [
        +        "domain"
        +      ],
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Optional exact structural scope using domain plus area_ids and/or floor_ids, with optional exclude_entity_ids."
        +}
      • addedInput schema / properties / timeout_seconds
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 60,
        +      "minimum": 0,
        +      "type": "number"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • addedInput schema / properties / validate_first
        Added value: +{
        +  "default": true,
        +  "type": "boolean"
        +}
      • removedInput schema / required
        Removed value: -[
        -  "operations"
        -]
    • Changedha_call_service6 fields changed
      • addedInput schema / properties / data / description
        Added value: +"Extra service-call parameters beyond entity_id (e.g. {'temperature': 22} for climate.set_temperature). Also carries the raw command payload when ws_command is set. If entity_id is also present in data, the entity_id parameter wins."
      • addedInput schema / properties / domain / description
        Added value: +"Service domain (e.g. 'light', 'climate', 'automation'). Required for a service call; must be omitted when ws_command is set."
      • addedInput schema / properties / entity_id / description
        Added value: +"Entity ID(s) the service call targets — one ID ('light.living_room') or several comma-separated ('light.a,light.b'). Optional for services that don't target a specific entity. Must be omitted when ws_command is set."
      • addedInput schema / properties / return_response / description
        Added value: +"If True, the service's response data is returned once, as the top-level 'service_response' key — never nested inside 'result' (default: False). Must stay False when ws_command is set."
      • addedInput schema / properties / service / description
        Added value: +"Service name within domain (e.g. 'turn_on', 'set_temperature', 'trigger'). Required for a service call; must be omitted when ws_command is set."
      • addedInput schema / properties / wait / description
        Added value: +"If True (default), wait for the entity state to change before returning. Applies only to state-changing services called with a single entity_id. A comma-separated multi-target does not get confirmed by this: it falls through to a legacy path that polls for the literal composite entity_id and times out after 10s. Set wait=False for multi-target calls."
    • Changedha_config_get_dashboard1 field changed
      • changedInput schema / properties / include_screenshot / description
        Previous value: -"Get mode only: also return rendered image(s) of the dashboard for visual verification. Requires the 'dashboard screenshot' beta feature + engine add-on/sidecar. If the feature is disabled the config is returned with a warning; if the engine is configured but the render fails, the call errors (the screenshot is the requested payload). Ignored in list/search mode."New value: +"Get mode only: also return rendered image(s) of the dashboard for visual verification. Requires the 'dashboard screenshot' beta feature + engine add-on/sidecar. If the feature is disabled the config is returned with a warning; if the engine is configured but the render fails, the call errors (the screenshot is the requested payload). Ignored in list/search mode. When you already have the config and only need the render, use the dedicated ha_get_dashboard_screenshot tool (registered when the same beta feature is on) — it returns images without echoing the config."
    • Changedha_config_set_dashboard1 field changed
      • changedInput schema / properties / return_screenshot / description
        Previous value: -"After writing, also return rendered image(s) of the dashboard so you can see what it looks like in a single call (the dashboard creation/iteration loop). Requires the 'dashboard screenshot' beta feature + engine add-on/sidecar; if unavailable, the write result is returned with a warning."New value: +"After writing, also return rendered image(s) of the dashboard so you can see what it looks like in a single call (the dashboard creation/iteration loop). Requires the 'dashboard screenshot' beta feature + engine add-on/sidecar; if unavailable, the write result is returned with a warning. For visual re-checks after the write (no config round-trip), use the dedicated ha_get_dashboard_screenshot tool instead."
    • Changedha_config_set_helper1 field changed
      • changedInput schema / properties / config / description
        Previous value: -"Config dict for flow-based helper types and helper_type='config_subentry' (template, group, utility_meter, derivative, min_max, threshold, integration, statistics, trend, random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat, history_stats, mold_indicator). Ignored for simple helper types. Field set is delivered as data_schema on the first validation error."New value: +"Config dict for flow-based helper types and helper_type='config_subentry' (template, group, utility_meter, derivative, min_max, threshold, integration, statistics, trend, random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat, history_stats, mold_indicator). Ignored for simple helper types. On update it is a patch: a field you omit keeps its current value, and a field set to null is cleared where the schema allows that field to be empty. A field two steps declare gets your one value both times; pass step_values={'<step_id>': {'<field>': <value>}} to give a step its own value, or to leave it out of that step; a LIST of those objects supplies one per encounter when the flow presents a step more than once. Field set is delivered as data_schema on the first validation error."
    • Changedha_get_app3 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"Search filter for add-on names/descriptions (only for source='available')"New value: +"App (add-on) name/description filter (only for source='available')"
      • changedInput schema / properties / slug / description
        Previous value: -"Add-on slug for detailed info (e.g., '<prefix>_nodered'). Slug prefixes vary by add-on repository — omit to list all add-ons and discover the actual installed slug."New value: +"App (add-on) slug for detailed info (e.g., '<prefix>_nodered'). Slug prefixes vary by app repository — omit to list all apps and discover the actual installed slug."
      • changedInput schema / properties / source / description
        Previous value: -"Add-on source: 'installed' (default) for currently installed add-ons, 'available' for add-ons in the store that can be installed."New value: +"App (add-on) source: 'installed' (default) for currently installed apps, 'available' for apps in the store that can be installed."
    • Changedha_get_logs1 field changed
      • addedInput schema / properties / offset / description
        Added value: +"Page deeper into source='logbook' and source='error_log' (ignored for other sources). On error_log it counts raw log lines back from the newest entry; pass the response's 'next_offset' to continue while 'has_more' is true."
    • Changedha_get_operation_status2 fields changed
      • addedInput schema / properties / timeout_seconds / minimum
        Added value: +0
      • changedInput schema / properties / timeout_seconds / type
        Previous value: -"integer"New value: +"number"
    • Changedha_get_overview1 field changed
      • changedInput schema / properties / fields / description
        Previous value: -"Return only the specified top-level response keys to reduce response size (e.g. [\"system_info\", \"domains\"]). None = full response (default). Available keys: success, system_summary, domain_stats, area_analysis, ai_insights, pagination, partial, warnings, device_types, service_availability, system_info, notification_count, notifications, repair_count, dismissed_repair_count, repairs, repairs_error, tool_discovery, settings_url, settings_url_hint, read_only_mode, read_only_mode_hint, ha_mcp_update. Note: ``settings_url`` (stdio mode), ``settings_url_hint`` (HTTP/Docker/OAuth mode), the ``read_only_mode`` / ``read_only_mode_hint`` pair (only while Read Only Mode is on), and ``ha_mcp_update`` (when an update check applies) are emitted regardless of ``fields=`` projection so the settings page, the active mode, and a newer ha-mcp release stay discoverable; see the tool description."New value: +"Return only the specified top-level response keys to reduce response size (e.g. [\"system_info\", \"domains\"]). None = full response (default). Available keys: success, system_summary, domain_stats, area_analysis, ai_insights, pagination, partial, warnings, device_types, service_availability, system_info, notification_count, notifications, repair_count, dismissed_repair_count, repairs, repairs_error, tool_discovery, settings_url, settings_url_hint, read_only_mode, read_only_mode_hint, ha_mcp_update. Note: ``settings_url`` (stdio mode), ``settings_url_hint`` (standalone HTTP/Docker mode), the ``read_only_mode`` / ``read_only_mode_hint`` pair (only while Read Only Mode is on), and ``ha_mcp_update`` (when an update check applies) are emitted regardless of ``fields=`` projection so the settings page, the active mode, and a newer ha-mcp release stay discoverable; see the tool description."
    • Changedha_manage_app12 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Lifecycle mode: run a Supervisor add-on action. One of 'install', 'uninstall', 'start', 'stop', 'restart', 'rebuild', 'update'. 'install'/'update' require the add-on's repository to be registered (it appears in ha_get_app(source='available')). Store-repository mode: 'add_repository' / 'remove_repository' register or unregister a custom add-on store repository — these use the 'repository' param instead of 'slug'. Mutually exclusive with path / config parameters / array_patch. HA OS / Supervised only."New value: +"Lifecycle mode: run a Supervisor app (add-on) action. One of 'install', 'uninstall', 'start', 'stop', 'restart', 'rebuild', 'update'. 'install'/'update' require the app's repository to be registered (it appears in ha_get_app(source='available')). Store-repository mode: 'add_repository' / 'remove_repository' register or unregister a custom app store repository — these use the 'repository' param instead of 'slug'. When ha-mcp runs as an app, it can update other apps but cannot update its own running slug; update ha-mcp from the Home Assistant Apps UI. Mutually exclusive with path / config parameters / array_patch. HA OS / Supervised only."
      • changedInput schema / properties / array_patch / description
        Previous value: -"Array-patch mode: atomically GET a JSON array endpoint, apply ordered ops, then POST the mutated array back. Requires 'path'; mutually exclusive with body / websocket / offset / limit and config params. See the docstring Examples and ha_get_skill_guide for op shapes."New value: +"Array-patch mode: atomically GET a JSON array endpoint, apply ordered ops, then POST the mutated array back. Requires 'path'; mutually exclusive with body / websocket / offset / limit and config params. Use ha_get_skill_guide for operation shapes."
      • changedInput schema / properties / auto_update / description
        Previous value: -"Config mode: Enable or disable automatic updates for this add-on."New value: +"Config mode: Enable or disable automatic updates for this app (add-on)."
      • changedInput schema / properties / network / description
        Previous value: -"Config mode: Host port mappings (e.g., {'5800/tcp': 8081})."New value: +"Config mode: Complete desired host-port override map (e.g., {'5800/tcp': 8081}). A non-empty map replaces current overrides, so omitted entries are cleared. Omit 'network' to leave mappings unchanged. An empty map is ignored and does not by itself select config mode."
      • changedInput schema / properties / options / description
        Previous value: -"Config mode: Add-on configuration values (the 'Configuration' tab in the UI)."New value: +"Config mode: App (add-on) configuration values (the 'Configuration' tab in the UI)."
      • changedInput schema / properties / path / description
        Previous value: -"Proxy mode: API path relative to the add-on root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode; mutually exclusive with config parameters."New value: +"Proxy mode: API path relative to the app (add-on) root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode; mutually exclusive with config parameters."
      • changedInput schema / properties / port / description
        Previous value: -"Proxy mode only. Connect to this port instead of the Ingress port. Use ha_get_app(slug='...') to find available ports."New value: +"Proxy mode only. Connect to this port instead of the Ingress port. Use ha_get_app(slug='...') to find available ports. Some apps, including Node-RED, reject direct access unless their leave_front_door_open option is enabled and the app is restarted; related errors include an actionable, security-qualified ha_manage_app options command."
      • changedInput schema / properties / repository / description
        Previous value: -"Store-repository mode only (action='add_repository' or 'remove_repository'). For add_repository: the repository URL (e.g., 'https://github.com/balloob/home-assistant-addons'). For remove_repository: the repository slug (e.g., '0f1cc410', as shown in ha_get_app(source='available')). Required for those actions; ignored otherwise."New value: +"Store-repository mode only (action='add_repository' or 'remove_repository'). For add_repository: the repository URL (e.g., 'https://github.com/balloob/home-assistant-addons'). For remove_repository: the repository slug (e.g., '0f1cc410', as shown in ha_get_app(source='available')). Required for those actions; rejected otherwise."
      • changedInput schema / properties / request_headers / description
        Previous value: -"Proxy/array-patch mode: extra HTTP headers to send to the addon API. Useful for addon-specific requirements such as Node-RED's `Node-RED-Deployment-Type: full`. The proxy's internal framing (`X-Ingress-Path`, `X-Hass-Source`, `Cookie`, `Content-Type`) is layered on top, so caller-supplied values for those keys are overridden. Not valid in config or websocket mode."New value: +"Proxy/array-patch mode: extra HTTP headers for the app (add-on) API. Useful for app-specific requirements such as Node-RED's `Node-RED-Deployment-Type: full`. Ingress routing headers override caller values on Ingress routes; direct-port calls have no internal routing headers. `Content-Type` is derived from the body when supplied. Not valid in config or websocket mode."
      • changedInput schema / properties / slug / description
        Previous value: -"Add-on slug (e.g., '<prefix>_nodered', '<prefix>_frigate'). Slug prefixes vary by add-on repository — call ha_get_app() to discover the actual installed slug. Required for every mode except the store-repository actions (action='add_repository'/'remove_repository'), which use 'repository' instead and take no slug."New value: +"App (add-on) slug (e.g., '<prefix>_nodered', '<prefix>_frigate'). Slug prefixes vary by app repository — call ha_get_app() to discover the actual installed slug. Required for every mode except the store-repository actions (action='add_repository'/'remove_repository'), which use 'repository' instead and take no slug."
      • changedInput schema / properties / wait_for_close / description
        Previous value: -"Proxy mode only. WebSocket: True: wait for the server to close the stream (run-to-completion ops like an ESPHome compile/validate). False: return after the first response batch — use for a one-shot command/response or a bounded log capture on a channel that stays open (e.g. ESPHome '/ws'). Default: true."New value: +"Proxy mode only. WebSocket: True waits for the server to close a run-to-completion stream. False returns after the first response batch; use for one-shot command/response or bounded capture on a channel that stays open. Default: true."
      • changedInput schema / properties / websocket / description
        Previous value: -"Proxy mode only. Use WebSocket instead of HTTP — for an add-on's WebSocket API (e.g. the ESPHome dashboard's '/ws' command channel; see the docstring's ESPHome section). Sends 'body' as the initial message, collects responses. Default: false."New value: +"Proxy mode only. Use WebSocket instead of HTTP for an app (add-on) WebSocket API. Sends 'body' as the initial message and collects responses; command names and body schemas are app/version-specific. Default: false."
    • Changedha_manage_backup2 fields changed
      • changedInput schema / properties / domain / description
        Previous value: -"(edits.list / edits.delete) Filter auto-backups by domain (e.g. 'automation', 'helper_timer')."New value: +"(edits.create / edits.list / edits.delete) Filter auto-backups by domain (e.g. 'automation', 'helper_timer'). Required for edits.create."
      • changedInput schema / properties / entity_id / description
        Previous value: -"(edits.list / edits.delete) Filter auto-backups by entity ID."New value: +"(edits.create / edits.list / edits.delete) Filter auto-backups by entity ID. Required for edits.create."
    • Changedha_manage_theme5 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Theme operation: list installed themes or set the default theme."New value: +"Theme operation: 'list' installed themes, 'set' the backend default theme, or read/restore the screenshot engine account's own per-user theme with 'get_engine_theme' / 'set_engine_theme' (a different layer from the backend default)."
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "list",
        -  "set"
        -]New value: +[
        +  "list",
        +  "set",
        +  "get_engine_theme",
        +  "set_engine_theme"
        +]
      • addedInput schema / properties / expected_current
        Added value: +{
        +  "anyOf": [
        +    {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Guard for action='set_engine_theme': the stored theme is read immediately before the write and the write is skipped if it no longer equals this. Omitting this value or passing null both mean 'expect no stored theme', enforced like any other value; the guard is always applied unless force is set. Best-effort, not atomic -- Home Assistant exposes no conditional write, so a change landing between that read and the write is not caught. Pass the expected_current value quoted in the screenshot tool's warning."
        +}
      • addedInput schema / properties / force
        Added value: +{
        +  "default": false,
        +  "description": "action='set_engine_theme' only: skip the expected_current guard and overwrite unconditionally. Leave false unless you intend to discard whatever is stored.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / value
        Added value: +{
        +  "anyOf": [
        +    {
        +      "additionalProperties": true,
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Frontend user-data theme object when action='set_engine_theme', e.g. {'theme': '', 'dark': False}. An empty dict restores default/auto behavior. Take this verbatim from the warning a screenshot tool emitted."
        +}
    • Changedha_search4 fields changed
      • changedInput schema / properties / area_filter / description
        Previous value: -"Narrow entity-registry results to an area (id or name). Does not affect configuration search."New value: +"Narrow entity-registry results to an area (id, name, or alias), an exact floor (id, name, or alias), or an unambiguous close-spelling floor match; a floor match expands to all areas on that floor. Does not affect configuration search."
      • changedInput schema / properties / config_time_budget / description
        Previous value: -"Per-call override for the per-id config-fetch wall-clock budget (seconds). Replaces the per-type HAMCP_*_CONFIG_TIME_BUDGET defaults for the automation, script, AND scene branches when their bulk-fetch falls through to per-id Attempt-C. Use when a `partial: True` response names time-budget skipping. Stateless per-call: one caller raising the budget doesn't affect others. None = use the per-type env defaults."New value: +"Per-call override for the per-id config-fetch wall-clock budget (seconds). Replaces the per-type HAMCP_*_CONFIG_TIME_BUDGET defaults for the automation, script, AND scene branches. Use when a `partial: True` response names time-budget skipping. Stateless per-call: one caller raising the budget doesn't affect others. None = use the per-type env defaults."
      • changedInput schema / properties / query / description
        Previous value: -"What to search for (entity name fragment, free-text config term, entity_id). Searches BOTH the entity registry (entity_ids, friendly names, areas) AND configuration bodies (automation triggers/actions, script sequences, scene contents, helper bodies, dashboard cards) in one call. Use this for any find-something-in-HA question — entity OR config. Omit `query` to enumerate by `domain_filter`, `area_filter`, and/or `state_filter` alone (registry-listing mode); configuration-body search is skipped in that mode because there is no term to match against."New value: +"What to search for (entity name fragment, free-text config term, entity_id). Searches BOTH the entity registry (entity_ids, friendly names, areas) AND configuration bodies (automation triggers/actions, script sequences, scene contents, helper bodies, dashboard cards) in one call. Use this for any find-something-in-HA question — entity OR config. Pass the exact entity_id, not a name fragment, when checking what a rename or delete would break: that form reports automations, scripts and scenes referencing it even when their configuration could not be read. Omit `query` to enumerate by `domain_filter`, `area_filter`, and/or `state_filter` alone (registry-listing mode); configuration-body search is skipped in that mode because there is no term to match against."
      • changedInput schema / properties / result_fields / description
        Previous value: -"Project each entity-registry record to only the specified keys (e.g. [\"entity_id\", \"state\"]). None = full records. Base keys: entity_id, friendly_name, domain, state, score, match_type. Opt-in enrichment keys (joined on request): area, floor, labels, aliases. An unknown key is rejected."New value: +"Project each entity-registry record to only the specified keys (e.g. [\"entity_id\", \"state\"]). None = full records. Base keys: entity_id, friendly_name, domain, state, score, match_type. Opt-in enrichment/membership keys (computed on request): area, floor, labels, aliases, is_group, member_entity_ids. Membership is recognized only when HA explicitly exposes a valid group_entities or legacy entity_id collection; member IDs are sorted, direct (not recursively expanded), and omitted if visibility/include_hidden excludes a member. is_group remains true when member IDs are withheld; requesting member_entity_ids also retains is_group. An unknown key is rejected."
    • Changedha_set_integration1 field changed
      • changedInput schema / properties / config / description
        Previous value: -"Flow form data. With 'domain': input for the new integration's config flow. With 'entry_id' alone: input for the entry's options flow (updates its options). Multi-step flows consume keys per step; menu steps take 'next_step_id' — a string, or a list of successive selections for flows that present more than one menu (e.g. a menu revisited after each branch, ending in a finish option). The step's data_schema is returned on validation errors so field names can be corrected."New value: +"Flow form data. With 'domain': input for the new integration's config flow. With 'entry_id' alone: input for the entry's options flow (updates its options). Updating an existing entry — options or reconfigure — is a patch: a field you omit keeps its current value, and a field set to null is cleared where the integration's schema allows that field to be empty. Multi-step flows consume keys per step. A field two steps declare gets your one value both times; pass step_values={'<step_id>': {'<field>': <value>}} to give a step its own value, or to leave the field out of that step; a LIST of those objects supplies one per encounter when the flow presents a step more than once. Menu steps take 'next_step_id' — a string, or a list of successive selections for flows that present more than one menu (e.g. a menu revisited after each branch, ending in a finish option). The step's data_schema is returned on validation errors so field names can be corrected."
  5. 10 tool updatesv8.3.0
    • Changedha_config_list_helpers1 field changed
      • changedInput schema / properties / helper_type / anyOf
        Previous value: -[
        -  {
        -    "enum": [
        -      "input_button",
        -      "input_boolean",
        -      "input_select",
        -      "input_number",
        -      "input_text",
        -      "input_datetime",
        -      "counter",
        -      "timer",
        -      "schedule",
        -      "zone",
        -      "person",
        -      "tag",
        -      "all"
        -    ],
        -    "type": "string"
        -  },
        -  {
        -    "enum": [
        -      "template",
        -      "group",
        -      "utility_meter",
        -      "derivative",
        -      "min_max",
        -      "threshold",
        -      "integration",
        -      "statistics",
        -      "trend",
        -      "random",
        -      "filter",
        -      "tod",
        -      "generic_thermostat",
        -      "switch_as_x",
        -      "generic_hygrostat"
        -    ],
        -    "type": "string"
        -  }
        -]New value: +[
        +  {
        +    "enum": [
        +      "input_button",
        +      "input_boolean",
        +      "input_select",
        +      "input_number",
        +      "input_text",
        +      "input_datetime",
        +      "counter",
        +      "timer",
        +      "schedule",
        +      "zone",
        +      "person",
        +      "tag",
        +      "all"
        +    ],
        +    "type": "string"
        +  },
        +  {
        +    "enum": [
        +      "template",
        +      "group",
        +      "utility_meter",
        +      "derivative",
        +      "min_max",
        +      "threshold",
        +      "integration",
        +      "statistics",
        +      "trend",
        +      "random",
        +      "filter",
        +      "tod",
        +      "generic_thermostat",
        +      "switch_as_x",
        +      "generic_hygrostat",
        +      "history_stats",
        +      "mold_indicator"
        +    ],
        +    "type": "string"
        +  }
        +]
    • Changedha_config_set_helper2 fields changed
      • changedInput schema / properties / config / description
        Previous value: -"Config dict for flow-based helper types and helper_type='config_subentry' (template, group, utility_meter, derivative, min_max, threshold, integration, statistics, trend, random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat). Ignored for simple helper types. Field set is delivered as data_schema on the first validation error."New value: +"Config dict for flow-based helper types and helper_type='config_subentry' (template, group, utility_meter, derivative, min_max, threshold, integration, statistics, trend, random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat, history_stats, mold_indicator). Ignored for simple helper types. Field set is delivered as data_schema on the first validation error."
      • changedInput schema / properties / helper_type / enum
        Previous value: -[
        -  "counter",
        -  "config_subentry",
        -  "derivative",
        -  "filter",
        -  "generic_hygrostat",
        -  "generic_thermostat",
        -  "group",
        -  "input_boolean",
        -  "input_button",
        -  "input_datetime",
        -  "input_number",
        -  "input_select",
        -  "input_text",
        -  "integration",
        -  "min_max",
        -  "person",
        -  "random",
        -  "schedule",
        -  "statistics",
        -  "switch_as_x",
        -  "tag",
        -  "template",
        -  "threshold",
        -  "timer",
        -  "tod",
        -  "trend",
        -  "utility_meter",
        -  "zone"
        -]New value: +[
        +  "counter",
        +  "config_subentry",
        +  "derivative",
        +  "filter",
        +  "generic_hygrostat",
        +  "generic_thermostat",
        +  "group",
        +  "history_stats",
        +  "input_boolean",
        +  "input_button",
        +  "input_datetime",
        +  "input_number",
        +  "input_select",
        +  "input_text",
        +  "integration",
        +  "min_max",
        +  "mold_indicator",
        +  "person",
        +  "random",
        +  "schedule",
        +  "statistics",
        +  "switch_as_x",
        +  "tag",
        +  "template",
        +  "threshold",
        +  "timer",
        +  "tod",
        +  "trend",
        +  "utility_meter",
        +  "zone"
        +]
    • Removedha_get_addon
    • Addedha_get_app
    • Changedha_get_logs3 fields changed
      • changedInput schema / properties / order / description
        Previous value: -"Sort order for time-ordered sources (logbook, system, error_log, supervisor, system_service): 'newest' (default) returns most-recent first; 'oldest' returns chronological-first. Ignored for source='logger'."New value: +"Sort order for time-ordered sources (logbook, system, error_log, supervisor, system_service): 'newest' (default) returns most-recent first; 'oldest' returns chronological-first. Ignored for source='logger', and for source='error_log' with structured=True (that summary is ranked by occurrence count, not by time)."
      • addedInput schema / properties / structured
        Added value: +{
        +  "default": false,
        +  "description": "source='error_log' only. When True, return a deduplicated, component-grouped summary of the log (counted issues sorted by frequency) instead of raw text. Use this on busy instances where the raw log is large enough to exhaust context. Ignored for other sources.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / top_n
        Added value: +{
        +  "anyOf": [
        +    {
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Max distinct issues to return when structured=True (default 20, capped at 500). Bounds the response regardless of log size."
        +}
    • Removedha_manage_addon
    • Addedha_manage_app
    • Changedha_manage_pipeline7 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"Pipeline operation: list, get, create, update, or set_preferred."New value: +"Pipeline operation: list, get, create, update, set_preferred, or process."
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "list",
        -  "get",
        -  "create",
        -  "update",
        -  "set_preferred"
        -]New value: +[
        +  "list",
        +  "get",
        +  "create",
        +  "update",
        +  "set_preferred",
        +  "process"
        +]
      • addedInput schema / properties / agent_id
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "For process only, the conversation agent entity ID to answer, e.g. 'conversation.home_assistant'. Overrides the agent taken from pipeline_id; omit both for the default agent."
        +}
      • addedInput schema / properties / conversation_id
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "For process only, the conversation to continue. Returned in the response so follow-up sentences keep their context."
        +}
      • changedInput schema / properties / language / description
        Previous value: -"Pipeline language, e.g. 'en'."New value: +"Pipeline language, e.g. 'en'. For process, the language to recognise the sentence in."
      • changedInput schema / properties / pipeline_id / description
        Previous value: -"Assist pipeline ID. Required for get, update, and set_preferred."New value: +"Assist pipeline ID. Required for get, update, and set_preferred. Optional for process, where it selects the conversation agent and language that pipeline is configured with."
      • addedInput schema / properties / sentence
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Natural-language command to run through Assist. Required when action='process'. A matched intent executes, and with the built-in agent a sentence matching a conversation trigger runs that automation."
        +}
    • Changedha_remove_helpers_integrations1 field changed
      • changedInput schema / properties / helper_type / anyOf
        Previous value: -[
        -  {
        -    "enum": [
        -      "input_button",
        -      "input_boolean",
        -      "input_select",
        -      "input_number",
        -      "input_text",
        -      "input_datetime",
        -      "counter",
        -      "timer",
        -      "schedule",
        -      "zone",
        -      "person",
        -      "tag",
        -      "config_subentry",
        -      "template",
        -      "group",
        -      "utility_meter",
        -      "derivative",
        -      "min_max",
        -      "threshold",
        -      "integration",
        -      "statistics",
        -      "trend",
        -      "random",
        -      "filter",
        -      "tod",
        -      "generic_thermostat",
        -      "switch_as_x",
        -      "generic_hygrostat"
        -    ],
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "enum": [
        +      "input_button",
        +      "input_boolean",
        +      "input_select",
        +      "input_number",
        +      "input_text",
        +      "input_datetime",
        +      "counter",
        +      "timer",
        +      "schedule",
        +      "zone",
        +      "person",
        +      "tag",
        +      "config_subentry",
        +      "template",
        +      "group",
        +      "utility_meter",
        +      "derivative",
        +      "min_max",
        +      "threshold",
        +      "integration",
        +      "statistics",
        +      "trend",
        +      "random",
        +      "filter",
        +      "tod",
        +      "generic_thermostat",
        +      "switch_as_x",
        +      "generic_hygrostat",
        +      "history_stats",
        +      "mold_indicator"
        +    ],
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
    • Changedha_set_integration6 fields changed
      • addedInput schema / properties / confirm_token
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Requires reconfigure=True. A token from a reconfigure preflight; applies the change. Any token still matching the entry's current state and the same requested config is accepted, so a token stays valid while nothing moves."
        +}
      • addedInput schema / properties / expected_device_id
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Requires reconfigure=True. Device registry ID the entry must still own, before and after the change."
        +}
      • addedInput schema / properties / expected_entity_ids
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Requires reconfigure=True. Exact entity IDs that must remain associated with the entry."
        +}
      • addedInput schema / properties / expected_mac
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Requires reconfigure=True. MAC or IEEE the entry's device must still report."
        +}
      • addedInput schema / properties / expected_unique_id
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Requires reconfigure=True, AND the ha_mcp_tools custom component: Home Assistant does not expose a config entry's unique_id over its API. Without the component this is rejected — anchor on expected_device_id, expected_mac or expected_entity_ids instead."
        +}
      • addedInput schema / properties / reconfigure
        Added value: +{
        +  "default": false,
        +  "description": "Use the existing config entry's official reconfigure flow (its connection settings) instead of its options flow. Without confirm_token this is a read-only preflight that returns one.",
        +  "type": "boolean"
        +}
  6. 3 tool updatesv8.2.0
    • Changedha_call_service1 field changed
      • changedInput schema / properties / verbose / description
        Previous value: -"Return HA's raw service response unchanged (default: False). Use as an escape hatch when you need the full propagation chain or raw attribute payload (debug / inspection). WARNING: brings back token-bloat for nested-group targets — prefer result_fields / result_attribute_keys for targeted control."New value: +"Return HA's raw changed-state records unchanged (default: False). Use as an escape hatch when you need the full propagation chain or raw attribute payload (debug / inspection). With return_response=True the response data still surfaces once as the top-level service_response key, never nested in result. WARNING: brings back token-bloat for nested-group targets — prefer result_fields / result_attribute_keys for targeted control."
    • Changedha_manage_hacs3 fields changed
      • changedInput schema / properties / action / description
        Previous value: -"'download' to install/update, or 'add_repository'"New value: +"'download' to install/update, 'add_repository' to register a custom repo, 'remove' to uninstall a downloaded repo, or 'update_information' to refresh a repository's release data from GitHub"
      • changedInput schema / properties / action / enum
        Previous value: -[
        -  "download",
        -  "add_repository"
        -]New value: +[
        +  "download",
        +  "add_repository",
        +  "remove",
        +  "update_information"
        +]
      • changedInput schema / properties / repository_id / description
        Previous value: -"Numeric HACS ID or 'owner/repo' path (action='download')"New value: +"Numeric HACS ID or 'owner/repo' path (action='download' / 'remove' / 'update_information')"
    • Changedha_set_integration1 field changed
      • changedInput schema / properties / config / description
        Previous value: -"Flow form data. With 'domain': input for the new integration's config flow. With 'entry_id' alone: input for the entry's options flow (updates its options). Multi-step flows consume keys per step; menu steps take 'next_step_id'. The step's data_schema is returned on validation errors so field names can be corrected."New value: +"Flow form data. With 'domain': input for the new integration's config flow. With 'entry_id' alone: input for the entry's options flow (updates its options). Multi-step flows consume keys per step; menu steps take 'next_step_id' — a string, or a list of successive selections for flows that present more than one menu (e.g. a menu revisited after each branch, ending in a finish option). The step's data_schema is returned on validation errors so field names can be corrected."
  7. 78 tool updatesv7.14.2
    • First observedha_bulk_control
    • First observedha_call_event
    • First observedha_call_service
    • First observedha_config_delete_dashboard
    • First observedha_config_delete_dashboard_resource
    • First observedha_config_get_automation
    • First observedha_config_get_calendar_events
    • First observedha_config_get_category
    • First observedha_config_get_dashboard
    • First observedha_config_get_label
    • First observedha_config_get_scene
    • First observedha_config_get_script
    • First observedha_config_list_dashboard_resources
    • First observedha_config_list_groups
    • First observedha_config_list_helpers
    • First observedha_config_remove_automation
    • First observedha_config_remove_calendar_event
    • First observedha_config_remove_category
    • First observedha_config_remove_group
    • First observedha_config_remove_label
    • First observedha_config_remove_scene
    • First observedha_config_remove_script
    • First observedha_config_set_automation
    • First observedha_config_set_calendar_event
    • First observedha_config_set_category
    • First observedha_config_set_dashboard
    • First observedha_config_set_dashboard_resource
    • First observedha_config_set_group
    • First observedha_config_set_helper
    • First observedha_config_set_label
    • First observedha_config_set_scene
    • First observedha_config_set_script
    • First observedha_eval_template
    • First observedha_get_addon
    • First observedha_get_automation_traces
    • First observedha_get_blueprint
    • First observedha_get_camera_image
    • First observedha_get_device
    • First observedha_get_entity
    • First observedha_get_entity_exposure
    • First observedha_get_hacs_info
    • First observedha_get_history
    • First observedha_get_integration
    • First observedha_get_logs
    • First observedha_get_operation_status
    • First observedha_get_overview
    • First observedha_get_skill_guide
    • First observedha_get_state
    • First observedha_get_system_health
    • First observedha_get_todo
    • First observedha_get_zone
    • First observedha_import_blueprint
    • First observedha_list_floors_areas
    • First observedha_list_services
    • First observedha_manage_addon
    • First observedha_manage_backup
    • First observedha_manage_energy_prefs
    • First observedha_manage_hacs
    • First observedha_manage_pipeline
    • First observedha_manage_radio
    • First observedha_manage_theme
    • First observedha_manage_updates
    • First observedha_reload_core
    • First observedha_remove_area_or_floor
    • First observedha_remove_device
    • First observedha_remove_entity
    • First observedha_remove_helpers_integrations
    • First observedha_remove_todo_item
    • First observedha_remove_zone
    • First observedha_report_issue
    • First observedha_restart
    • First observedha_search
    • First observedha_set_area_or_floor
    • First observedha_set_device
    • First observedha_set_entity
    • First observedha_set_integration
    • First observedha_set_todo_item
    • First observedha_set_zone

TDQS

A3.8/5.0

Scored across 77 tools

Disambiguation3/5

Many tools target distinct resources, but the set contains numerous overlapping families: ha_call_service vs dedicated service tools, ha_search vs specific get tools, six remove_* variants for helpers/groups/entities, and paired get/set/manage tools for integrations, HACS, updates, and devices. The descriptions provide strong when-to-use guidance, so most ambiguity is mitigated, but choosing among the many removal and group-management tools remains error-prone.

Naming Consistency3/5

Prefixes vary widely: ha_config_set/get/remove/list/delete, plain ha_set/get/remove/list, ha_manage_*, plus one-off names like ha_restart, ha_reload_core, ha_search, and ha_eval_template. Verb placement and word choice (set vs config_set, remove vs delete) are inconsistent, though the ha_config_* and ha_manage_* families are internally stable. Mixed conventions but still readable.

Tool Count1/5

77 tools is far beyond a practical scoped surface for an agent, with many operations that could be consolidated (e.g., six remove_* helpers, multiple get/list variants, several dashboard-resource tools). The rubric's extreme-mismatch anchor applies at 50+ tools, and this set exceeds that threshold substantially.

Completeness5/5

The surface covers nearly every Home Assistant subsystem: automations, scripts, scenes, helpers, dashboards, devices, entities, areas/floors, labels, categories, calendars, todos, zones, HACS, integrations, updates/repairs, radios, backups, logs, traces, templates, energy, pipelines, blueprints, themes, and core system operations. CRUD and lifecycle coverage is deep, with no obvious dead ends.

Maintenance

ActivityNo data
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server and Home Assistant add-on that enables AI assistants to manage smart homes by creating automations, designing dashboards, and interacting with entities. It features native access to Home Assistant APIs, built-in Git versioning for safe rollbacks, and full management of HACS integrations.
    632
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    MCP server for full Home Assistant control, enabling AI agents to manage dashboards, automations, files, apps, entities, and more via REST API, WebSocket, and SSH.
    66
    129 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server that enables AI assistants to control Home Assistant via natural language, including device control, automation management, and system configuration.
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    A self-hosted MCP server for Home Assistant that exposes full control over entity states, service calls, history, templates, and areas via local stdio, enabling AI assistants to manage your smart home.
    9
    99 npm
    MIT