Home Assistant MCP Server
OfficialThis server lets an AI assistant inspect, control, configure, and maintain a Home Assistant smart-home installation.
Search and inspect entities, states, history, statistics, logs, automation traces, devices, areas, floors, integrations, and system health.
Control devices and call any Home Assistant service or event, including bulk operations and operation status checks.
Create and manage automations, scripts, scenes, blueprints, helpers, groups, labels, categories, zones, calendars, and todo lists.
Build and edit Lovelace dashboards, including resources and screenshot-based verification where available.
Manage Home Assistant system tasks: backups, restarts, reloads, updates, apps/add-ons, HACS/Marketplace, themes, energy preferences, and Assist pipelines.
Manage radios and device networks for Z-Wave, Zigbee, Matter, and Thread, including diagnostics and some destructive actions.
Edit entity/device registry metadata, exposure, areas, names, labels, and enabled/disabled state.
Use beta file/YAML tools for allowed file operations and
configuration.yamledits when enabled.Evaluate Jinja2 templates, test automation conditions, and access bundled Home Assistant best-practice guidance/tool discovery.
Enables AI assistants to interact with Home Assistant, allowing natural language control of smart home devices, state queries, service execution, and automation management.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Home Assistant MCP Serverturn off the kitchen lights"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Breaking change (v7.3.0):
ha_config_set_yamlhas been moved to beta.
HA-MCP โ Manage Home Assistant with AI

๐ Get Started
The recommended way to run ha-mcp is the HA-MCP integration. It installs into Home Assistant through HACS (the built-in Marketplace on Home Assistant 2026.11+), 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: setup asks for an administrator access token and how clients may connect.
Add it to Home Assistant via HACS or, on Home Assistant 2026.11+, the Marketplace (the preferred install):
Quick start:
The setup below is for version 9.0.0 (October 28, 2026) and later; earlier versions show a shorter setup form without the administrator account step.
Install the HA-MCP integration (requires Home Assistant 2026.8.0 or newer; see compatibility details). On Home Assistant 2026.8โ2026.10, open HACS, search for HA-MCP, then Download. On Home Assistant 2026.11 or newer, open Settings โ Marketplace, accept its first-run warning, and search for HA-MCP, then Install. HA-MCP is in the default catalog, so installing it and receiving its updates do not require connecting GitHub or adding a custom repository. The badge above also opens its catalog page. Already installed from the earlier
homeassistant-ai/ha-mcp-integrationrepository? It keeps receiving the same releases. On the Marketplace, keep GitHub connected for its background checks on that custom repository. Older installed releases may still appear as HA-MCP Custom Component; searching for HA-MCP matches both names.Restart Home Assistant.
Decide which administrator account the server acts as. Either create a long-lived access token on your own account (your profile โ Security โ Long-lived access tokens โ Create token; it works from anywhere if it leaks, and the server's changes show under your name), or, safer, create an account just for HA-MCP under Settings โ People โ Add person with Allow login, Local access only and Administrator on. Never use your own login for the second option.
Go to Settings โ Devices & Services โ Add Integration, search for HA-MCP and choose HA-MCP Server. Paste your token, or enter the user name and password of the account you made for HA-MCP (HA-MCP creates its token), pick how clients may connect โ Remote access through Home Assistant (start with Secret URL, the simplest; Home Assistant sign-in adds a login) and/or Network access: Local network โ then click Submit. Both start disabled, so a client on another device needs one of them.
Copy the connect URL from the entry's Configure screen (Settings โ Devices & Services โ โHA-MCP โ Manage Home Assistant with AIโ โ HA-MCP Server โ Configure). A notification confirms the server started and points you there.
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? Leave remote access disabled at setup (or 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 Authentication mode to
ha_authto require a Home Assistant account sign-in instead of using the secret URL as the credential.Manual install (no HACS or Marketplace): copy
custom_components/ha_mcp_tools/from this repository into your Home Assistantconfig/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.
Add the repository to your Home Assistant instance:
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.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.)
Open the Logs tab to find your unique MCP URL.
Connect your AI client to that URL โ no token or credential setup needed.
โ ๏ธ 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@latestentry withHOMEASSISTANT_URL/HOMEASSISTANT_TOKENalongside 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-mcpin 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-mcppackage withuvx 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_urloption). 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.
Install the MCP Server app (see above) and the Webhook Proxy app from the same store
Start the webhook proxy and restart Home Assistant when prompted
Copy the webhook URL from the app logs:
MCP Server URL (remote): https://xxxxx.ui.nabu.casa/api/webhook/mcp_xxxxxxxxConfigure 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.
Go to claude.ai and sign in (or create a free account)
Open Terminal and run:
curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-macos.sh | shDownload Claude Desktop (or restart: Claude menu โ Quit)
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:
Install the community Claude Desktop for Linux build and sign in with a free claude.ai account
Open Terminal and run:
curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-linux.sh | shRestart Claude Desktop, then ask: "Can you see my Home Assistant?"
Claude Code โ official CLI, requires a paid Claude plan:
Install Claude Code:
curl -fsSL https://claude.ai/install.sh | bashConfigure ha-mcp, then run
claude:curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install.sh | sh -s -- --claude-codeStart
claude, run/mcpto confirm, then ask: "Can you see my Home Assistant?"
Go to claude.ai and sign in (or create a free account)
Open Windows PowerShell (from Start menu) and run:
irm https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-windows.ps1 | iexDownload Claude Desktop (or restart: File โ Exit)
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) |
|
Areas & Floors |
|
Assist |
|
Automations |
|
Blueprints |
|
Calendar |
|
Camera |
|
Dashboard |
|
Dashboards |
|
Developer |
|
Device Registry |
|
Energy |
|
Entity Registry |
|
Files |
|
Groups |
|
HACS |
|
Helper Entities |
|
History & Statistics |
|
Integrations |
|
Labels & Categories |
|
Matter |
|
Scenes |
|
Scripts |
|
Search & Discovery |
|
Service & Device Control |
|
System |
|
Todo Lists |
|
Utilities |
|
Zones |
|
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 integration 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 |
| Safely add, replace, or remove top-level YAML keys in |
| List files in allowed directories |
| Read files from allowed paths (config YAML, logs, and allowed directories) |
| Write files to allowed directories |
| 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 integration:
Requires Home Assistant 2026.8.0 or newer through HACS or the Marketplace; see compatibility details for manual installs. On Home Assistant 2026.8โ2026.10: open HACS, search for HA-MCP, and Download. On 2026.11 or newer: open Marketplace, accept the first-run warning, search for HA-MCP, and Install. The main repository is in the default catalog for both. 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, 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_ctxis 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 |
| BM25 English-keyword search across all tools, pinned ones included. A hidden tool comes back compact: name, one-line description (the docstring's first paragraph), a one-line |
| Execute a |
| Execute a write tool that creates or updates data. |
| 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 |
|
| Replace full tool catalog with search-based discovery (tools deferred behind on-demand search). |
|
| Max hidden tools returned per |
| 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. in ChatGPT open Settings, then the plugin settings, select the connector and click Refresh tools; disconnect and reconnect a Claude Desktop custom connector; refresh the tools list of a claude.ai connector; or quit and reopen Claude Desktop for a server inclaude_desktop_config.json), then start a new conversation. 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 a refresh, delete the connector and create a new one with a different name. The FAQ has the full steps.
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.
Per-URL tool catalog
ENABLE_TOOL_SEARCH defaults to off, and it sets the catalog of the main endpoint only. When several clients share one server, give each the URL for the catalog it needs: a client with built-in tool search keeps the full list, a small-context client gets the search tools. The suffixes are fixed overrides that ignore ENABLE_TOOL_SEARCH:
Main endpoint (follows ENABLE_TOOL_SEARCH): https://example.com/private_your_secret
Always the full catalog: https://example.com/private_your_secret/catalog/full
Always the search tools: https://example.com/private_your_secret/catalog/search
Read-only, search tools: https://example.com/private_your_secret/readonly/catalog/searchEach URL keeps its catalog for the life of the connection, so a client picks its catalog only by the URL it is given. Reconnect the client after changing its URL so it refreshes its tool list. Home Assistant webhook URLs accept the same suffixes, alone or after /readonly: https://your-ha.example/api/webhook/<webhook-id>/catalog/search. This needs an embedded integration or Webhook Proxy app (add-on) release that serves them, with a matching MCP server; an older one returns 404 for a suffixed URL. Stdio runs one client per server process, so set ENABLE_TOOL_SEARCH in that client's server environment instead.
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/readonlyOAuth 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 [...]witheq/in/regex/contains/exists/...) inspired the per-tool approval rule schema (#966).
๐ฅ Contributors
Maintainers
@julienld โ Project creator.
@sergeykad โ Core maintainer.
@kingpanther13 โ Core maintainer.
@Patch76 โ Core maintainer.
Contributors
@bigeric08 โ Explicit
mcpdependency for protocol version 2025-11-25 support.@airlabno โ Support for
datafield 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_overviewfor 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_searchtimeout 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_integrationschema 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_transformsupport for automations and scripts.@gcormier โ Windows installer improvements: removed unused variable and fixed terminal closing after install.
@ekobres โ Feature flags for
HAMCP_ENABLE_FILESYSTEM_TOOLSand the (since removed)HAMCP_ENABLE_CUSTOM_COMPONENT_INTEGRATIONin 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(nowha_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_eventtool (#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
Contextin long-running tools (#1124); tool-discovery / categorized-search docs (#1123).@fnordpig โ Config subentry support (#1393) and Assist pipeline management tool (#1392).
@paul43210 โ
array_patchmode inha_manage_appfor 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_projecttool exposing KNX group addresses from an uploaded ETS project file.@cbowns โ Fixed inconsistent hyphen in setup.astro Codex CLI docs.
@Shaan-alpha โ Extended
ha_restartknown-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
rrulesupport inha_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_issuetool generates, or a reason why there is none; without either they are closed after 24 hours.
Star History
Available Tools
77 toolsha_bulk_controlBulk ControlADestructive
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| action | No | One device action applied to every resolved leaf. | |
| dry_run | No | Selector mode only; True is rejected in operations mode. | |
| parallel | No | Dispatch operations concurrently (default) or one at a time when False. | |
| selector | No | Optional exact structural scope using domain plus area_ids and/or floor_ids, with optional exclude_entity_ids. | |
| operations | No | Explicit entity operations. Each item requires exact entity_id and action. Use action='off', not service='turn_off'. | |
| parameters | No | Optional action parameters for selector mode. | |
| validate_first | No | 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. | |
| timeout_seconds | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructive=true/idempotent=false, and the description goes substantially beyond them: fail-closed behavior when every item fails, the group-plus-own-member conflict that silently drops nothing (nothing dispatched), the non-transactional per-leaf failure model, the 100-entity selector cap, and dry_run preview semantics. This is exactly the extra context an agent needs before a destructive bulk call.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose and exclusions, then splits modes into labeled paragraphs, which is well structured. However, the trailing Claude Desktop timeout paragraph is long and anecdotal, and mode exclusivity rules are restated in both the description and the schema, so some text does not fully earn its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter destructive tool, the definition covers both execution modes, failure/abort semantics, exclusion mechanics, batch-size limits, and preview. An output schema exists so return values need no explanation, and annotations carry the safety profile; no gap remains that would cause a misinvocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 schema already documents mode exclusivity (top-level validate_first/timeout_seconds/dry_run rejected in operations mode). The description adds genuine cross-parameter semantics not in the schema: the group/aggregate targeting conflict and its required exclude_entity_ids remedy, plus the >100-entity tightening path โ both directly constrain how selector parameters may be combined.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Manage explicit operations or one deterministic structural bulk action') and immediately disambiguates against named siblings ha_call_service and ha_search. An agent can distinguish this tool from every neighboring tool 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Contains an explicit 'When NOT to use' clause naming the two alternatives and the conditions that select them, then explains when to prefer operations mode vs selector mode (exclusions after recursive expansion, group/aggregate conflicts). 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_call_eventCall EventADestructive
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(entity_id=..., 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; a successful dispatch reports the bus accepted the event and how many listeners were registered for it, not that any of them acted. A timeout means dispatch is unconfirmed. listener_count covers the exact event type; wildcard_listener_count covers match-all listeners (the recorder is one, and it only stores the event). The counts come from the component's dispatch reply when it serves the fire, otherwise from a GET /api/events lookup taken before dispatch; a route that cannot report them omits both fields.
The response carries the event's context_id (and context) when Home Assistant reports it. An automation triggered by this event records that id as the parent_id of its run's context, so looking up that automation trace proves whether a listener acted โ which the counts cannot show, because an event trigger that filters on event_data still registers as a listener when the payload does not match.
Events fire in-process through the ha_mcp_tools component when it is installed, otherwise over Home Assistant's own WebSocket fire_event command; both carry the event type in the message body, so any type Core accepts fires as written and Core's rejection comes back as the error text (for example its 64-character event-type limit). state_changed alone keeps the REST route, because only that route rebuilds its old_state/new_state payloads into State objects; there event_type must be a plain name with no / \ ? # or % characters.
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| data | No | Optional event payload, delivered to subscribers as the event's data (trigger.event.data in an automation). | |
| event_type | Yes | Event type to fire, e.g. 'my_custom_event'. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (destructiveHint=true, non-idempotent) it discloses genuinely useful behavior: fire-and-forget semantics, what listener_count vs wildcard_listener_count mean, that a timeout means unconfirmed dispatch, and that context_id/parent_id is the only proof a listener acted. However, it spends most of its length on internal dispatch routing (in-process component vs WebSocket fire_event, REST exception for state_changed) and a Claude Desktop timeout workaround, which is operational trivia rather than behavior an agent needs to select or invoke the tool. The safety-relevant disclosure is diluted by this padding.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and exclusions are correctly front-loaded, but the body sprawls across five dense paragraphs, most of it covering dispatch internals, error-text provenance, and a client-specific timeout workaround. Several sentences (the recorder being a wildcard listener, the fallback GET /api/events lookup) do not earn their place in a tool description an agent must parse at selection time.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter fire-and-forget tool with an output schema and full param documentation, this covers more than enough: exclusions, error surfacing, count semantics, and trace-based verification. It loses a point because the volume of low-value routing detail and the confusing 'read the target back before repeating it' instruction make it harder to extract the operative guidance, and the client-specific paragraph reads as extraneous.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for both parameters, so event_type and data are already fully documented, establishing a baseline of 3. The description adds a real constraint beyond the schema (for state_changed the type must be a plain name without / \ ? # or %, and Core enforces a 64-character event-type limit), but only for one narrow route; it offers nothing extra about the data payload.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource: 'Execute a custom event on the Home Assistant event bus.' It then names what the event is for (event-triggered automations, Node-RED flows, custom integrations), which lets an agent distinguish it from service calls and automation runs 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit 'When NOT to use' block with two named alternatives and their exact invocation (ha_call_service for entities, ha_config_set_automation(entity_id=..., run_actions=True) to run an automation), plus a positive use case. Both the exclusion and the routing condition are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_call_serviceCall ServiceADestructive
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| data | No | 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. | |
| wait | No | 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. | |
| domain | No | Service domain (e.g. 'light', 'climate', 'automation'). Required for a service call. | |
| service | No | Service name within domain (e.g. 'turn_on', 'set_temperature', 'trigger'). Required for a service call. | |
| verbose | No | 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. | |
| entity_id | No | 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. | |
| ws_command | No | 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. | |
| result_fields | No | 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. | |
| return_response | No | If True, the service's response data is returned once, as the top-level 'service_response' key โ never nested inside 'result'. | |
| result_attribute_keys | No | 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). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (destructive=true, readOnly=false, idempotent=false), and the description layers on non-obvious behavior: default result compaction and exactly what it strips, the ws_command one-shot restriction with call_service/execute_script rejected, unavailability in Read Only Mode, and the multi-target/wait pitfall. The Claude Desktop timeout-before-retry workaround is unusual but operationally material.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then when-not/when-to, examples, compaction, the WebSocket escape hatch, and mode gating โ every section is headed and information-dense. The single detractor is the trailing Claude Desktop timeout paragraph, which is client-specific and sits far from the call semantics it relates to.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter catch-all with an output schema present, the description covers the routing decision, the two invocation modes, the read-only gate, and the result-shaping behavior that the output schema cannot express. Nothing an agent needs in order to choose and invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 and the schema already carries the parameter contract. The description still adds meaning the schema does not: worked examples showing positional domain/service plus data, and the domain.service pattern that governs how these two strings must be supplied. That is a modest but real lift above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('call any Home Assistant service... the catch-all escape hatch') and immediately frames itself against dedicated siblings, naming them with the exact arguments that select them (ha_config_set_automation, ha_config_set_script, ha_config_set_scene, ha_manage_app, ha_manage_updates, ha_set_integration). An agent can place this tool correctly 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-NOT-to-use (a dedicated tool covers the job) with a concrete routing table of alternatives, followed by an explicit when-to-use (a service no dedicated tool covers), the domain.service naming convention, and a pointer to ha_list_services for discovery. Both the positive and negative conditions are spelled out rather than inferred.
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 DashboardADestructiveIdempotent
Delete a storage-mode dashboard completely.
WARNING: This deletes the dashboard and all its configuration; with auto-backup on, ha_manage_backup(scope='edits', action='restore') re-creates it. YAML-mode dashboards are defined in configuration.yaml and cannot be deleted via the API.
HA internal IDs may differ from url_path (e.g. hyphens โ underscores); the tool resolves either form to the actual registry ID before deletion. 'default' is an alias for 'lovelace'.
EXAMPLES:
Delete dashboard: ha_config_delete_dashboard("mobile-dashboard")
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| url_path | Yes | Dashboard URL path or internal ID to delete (e.g., 'my-dashboard' or 'my_dashboard'). Both forms are accepted. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=true, and the description goes well beyond them: it states that all configuration is destroyed, that an auto-backup can re-create it, that YAML-mode dashboards are immune, and that IDs are resolved to a registry ID before deletion. This is exactly the context a caller needs before firing a destructive, irreversible-looking operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The warning is correctly front-loaded ahead of examples, and the examples block is tight. The closing Claude Desktop timeout paragraph is long and harness-specific rather than tool semantics, which dilutes an otherwise efficient description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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, the description covers the destructive blast radius, the backup recovery route, the YAML-mode limitation, ID/alias resolution, and a concrete example. Nothing an agent needs in order to call it safely is left implicit.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and already notes that both ID and url_path forms are accepted, so the description's added value is the nuance: hyphens may map to underscores internally, either form is resolved to the real registry ID, and 'default' is an alias for 'lovelace'. That meaning is genuinely absent from the schema, though the failure mode when neither form resolves is unstated.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Delete a storage-mode dashboard completely') and immediately narrows the scope to storage-mode, which distinguishes it from the sibling read/set dashboard tools and from the YAML-mode case. An agent can identify exactly what this operates on 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear conditions: storage-mode only, YAML-mode dashboards cannot be deleted via the API, and 'default' aliases 'lovelace'. It also points to a recovery path via ha_manage_backup(scope='edits', action='restore') and tells the agent to read the target back after a timeout. It stops short of naming the direct alternative for editing (ha_config_set_dashboard) or stating an explicit 'do not use this to update' exclusion, so not a full 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 ResourceADestructiveIdempotent
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| resource_id | Yes | Resource ID to delete. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=true, and readOnlyHint=false, so safety is covered. The description adds genuine beyond-schema context: the downstream impact (custom cards fail to load) and an operational caveat about Claude Desktop timeouts/hangs. That extra failure-mode detail is valuable, though the timeout workaround is more incidental than core behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose, warning, example, and prerequisite note are front-loaded and efficient. The closing Claude Desktop timeout paragraph is three sentences of situational troubleshooting that dilutes focus, costing some conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter destructive mutation with an output schema, the description covers purpose, prerequisite discovery, downstream risk, and an example. 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and there is only one required parameter with a clear schema description ('Resource ID to delete.'). The description only restates resource_id in the example, adding no format or lookup semantics beyond the schema, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('Delete a dashboard resource') and clarifies scope as removing a Home Assistant resource so it is no longer loaded on dashboards. It is clearly distinguishable from the sibling set_dashboard_resource / list_dashboard_resources tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs the agent to call ha_config_list_dashboard_resources() first to find the resource_id, and warns to ensure no dashboards depend on the resource before deleting. When-to-use and prerequisites are both covered, including a concrete example call.
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 ConfigARead-onlyIdempotent
Get a Home Assistant automation's stored configuration.
config is the body Home Assistant stores, as it stores it; its
config_hash stays the same across reads of an unchanged automation.
entity is the automation's entity registry row, validation holds
Home Assistant's status and error when the automation failed to load
(None when it loaded), and references lists the entities, devices,
areas, floors, labels and blueprint it uses. references is None
without the ha_mcp_tools component's automation_config bridge and
while Home Assistant has not loaded the automation (disabled, or it
failed to load); warnings says which. Without the bridge, related
holds Home Assistant's Related set for the automation instead, keyed
by item type: what it uses plus their devices, areas, floors and
integrations. automation_id is the resolved entity_id, falling back
to the input.
EXAMPLES:
Get automation: ha_config_get_automation("automation.morning_routine")
Get by config id: ha_config_get_automation("1712345678901")
For comprehensive automation documentation, use ha_get_skill_guide.
read inspect fetch view existing automation config triggers conditions actions get show detail
| Name | Required | Description | Default |
|---|---|---|---|
| entity_id | Yes | The automation's entity_id (e.g. 'automation.morning_routine') or its config id |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the description correctly spends its space on non-obvious traits: config_hash stability across reads of an unchanged automation, and the specific conditions (missing bridge, unloaded/disabled automation) under which references is None and related is substituted. Some of this repeats field semantics that an output schema would carry, which keeps it below 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose sentence is front-loaded and the examples are compact, but the middle paragraph is a dense run-on enumerating every return field, and the trailing keyword string ('read inspect fetch view existing automation config triggers conditions actions get show detail') is search-noise that adds nothing for an agent.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read tool with an output schema, the definition covers input forms, failure modes, bridge-dependent field fallbacks, and a pointer to the documentation skill โ an agent has enough to call it and interpret degraded results. It is somewhat over-complete on return-field detail while under-specifying how this differs from related read tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents the single parameter, so the baseline is 3. The description earns the extra point by explaining resolution behavior โ automation_id is the resolved entity_id, falling back to the raw input โ which the schema does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a specific verb and resource ('Get a Home Assistant automation's stored configuration') and scopes it to the stored body rather than a live state, which separates it from ha_config_get_script, ha_config_get_scene and ha_get_state in the sibling list.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the input forms accepted (entity_id like 'automation.morning_routine' or a config id) through two concrete examples, and routes documentation requests to ha_get_skill_guide. It does not say when to prefer ha_get_entity or ha_get_automation_traces instead, so the exclusion guidance is incomplete.
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 EventsARead-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")
| Name | Required | Description | Default |
|---|---|---|---|
| end | No | End datetime in ISO format (default: 7 days from now, not from start; pass end whenever you pass start) | |
| start | No | Start datetime in ISO format; omit it to start at the current time | |
| entity_id | Yes | Calendar entity ID (e.g., 'calendar.family') | |
| max_results | No | Maximum number of events to return |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already carry the read-only/idempotent safety profile, and the description adds real context on top: which fields come back, that uid and recurrence_id are only present when the calendar supplies them, and that those two fields are what the edit/remove tools consume. It does not discuss pagination or truncation behavior when max_results is hit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and scope, followed by return fields, sibling linkage, and two compact examples. Every sentence and example carries information an agent would otherwise have to infer.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a four-parameter read tool with annotations and an output schema, nothing needed to invoke it correctly is missing: defaults, date-range convention, entity discovery path, and downstream use of uid are all covered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 worked examples demonstrate the start/end pairing convention and the entity-only default call, which adds practical meaning beyond the field docs. max_results is left entirely to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (get) and resource (calendar events) plus the scoping dimension (within a time range). It is clearly separable from the sibling tools ha_config_set_calendar_event and ha_config_remove_calendar_event, which it even references by their use of uid/recurrence_id.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the default window (next week) and explicitly routes entity discovery to ha_search(domain_filter='calendar'), plus points at the edit/remove siblings. It does not state any explicit exclusion (e.g., when to prefer ha_get_history or a service call over this), so it falls short of a full 5.
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 CategoryARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| scope | Yes | Domain scope for categories (e.g., 'automation', 'script', 'scene', 'helpers'). | |
| category_id | No | ID of the category to retrieve. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description adds the meaningful behavioral nuance that omitting category_id changes the operation into a list, plus the domain-scoping concept. It does not mention auth/rate limits, but nothing is contradicted.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then branches, then context, then examples and alternatives. Every sentence earns its place; no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 prose. For a two-parameter, optionally-branching read tool, the description plus annotations plus examples cover everything an agent needs to select and invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 genuinely adds semantics: it spells out the dual effect of optional category_id, gives concrete scope values, and supplies two worked examples. This goes beyond restating the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get category info') and immediately clarifies the dual mode: list all categories for a scope, or fetch one by ID. The domain-scope sentence ('domain-scoped organizational groups for automations, scripts, scenes, and helpers') further anchors what a category is, distinguishing it from siblings like ha_config_get_label or ha_config_list_groups.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: use ha_config_set_category() to create/update and ha_set_entity(categories=...) to assign. The without/with category_id branches define exactly when each behavior triggers, leaving no inference required.
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 DashboardARead-onlyIdempotent
Get Lovelace dashboards: list them, read one config, or search their cards.
Not for a render alone: ha_get_dashboard_screenshot (screenshot beta feature) returns images without the config. Not for entity discovery: use ha_search.
The parameters pick the mode:
list_only=True: storage and YAML dashboards alike.
Any of query / entity_id / card_type / heading searches cards and header cards, AND-ing the criteria, in url_path or, when it is omitted, every storage dashboard. View badges answer entity_id and query, and heading excludes them. Cards nested at any depth count, including custom cards' own keys such as groups[].cards[].card. Nesting is found heuristically, under the keys cards, card, custom_fields and states; a card under any other key is read only as its parent's text, by query. Each match has a jq_path and a python_path to append to
configin ha_config_set_dashboard(python_transform=...), e.g. f'config{m["python_path"]}["icon"] = "mdi:x"'. Each match carries its dashboard's url_path and config_hash; a one-dashboard search also returns the config_hash at the top level.Otherwise the full config of url_path (the default dashboard when omitted) is returned; with view_path only that view, while config_hash still covers the full config. stored_config is false (config null) when Home Assistant holds no config for the dashboard.
Caveats: config_hash stays the same across reads of an unchanged config.
Searches stop at 200 matches (truncated). A search across dashboards
skips YAML dashboards (HA resolves !secret in them) and, without a
current ha_mcp_tools component, the default dashboard (with a warning);
name either by url_path.
Strategy dashboards have no stored cards to search. An unknown view_path
errors and lists the available ones.
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(query="light.bedroom")
Fields of a card type before writing one (omit card_type to list types): ha_config_get_dashboard(card_type="tile", describe=True)
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Search: text or entity ID contained in any value a card holds (case-insensitive); the matching values are listed under `matched`. | |
| heading | No | Search: cards whose heading/title contains this text (case-insensitive). | |
| describe | No | Return card_type's fields from HA's card editor | |
| url_path | No | Dashboard URL path (e.g. 'lovelace-home'); 'default' for the default dashboard. | |
| card_type | No | Search: cards of this type, e.g. 'tile'; 'badge' selects view badges instead (alone, it lists them all). With describe=True: the type to describe. | |
| entity_id | No | Search: cards whose entity/entities field holds this entity ID; '*' matches any run of characters (including none) and the whole ID must match (e.g. 'sensor.temperature_*'). | |
| list_only | No | List every dashboard's metadata instead of reading one; ignored when a search parameter is given. | |
| view_path | No | 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. | |
| force_reload | No | Get only: bypass the config cache (searches always read fresh config). | |
| include_config | No | Search: include each matched card's own config. A container's body includes its nested cards, which are also matches, so nested stacks multiply the payload. Withheld, with a warning, for a dashboard not provably storage-mode (a YAML config may carry resolved !secret values). | |
| include_screenshot | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Although annotations already mark this as a safe read operation, the description adds substantial behavior beyond them: config_hash stability, the 200-match truncation limit, YAML dashboard skipping for cross-dashboard searches, default-dashboard warnings, strategy dashboards lacking stored cards, and unknown view_path errors listing available views.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and exclusions, then organized by mode and caveats, with examples at the end. It is dense and long, but most sentences carry necessary operational detail for an 11-parameter tool; a slightly tighter structure would improve scanability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the 11 parameters, absence of an output schema, and rich read-only annotations, the description covers modes, caveats, error behavior, return-shape hints (config, view, view_index, config_hash, truncated), and examples. 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema coverage the baseline would be 3, but the description adds mode-selection semantics and cross-parameter interactions the schema does not: search parameters are AND-ed, entity_id supports wildcards, heading excludes badges, card_type='badge' lists view badges, describe=True uses card_type as a type to describe, and include_config has a storage-mode safety caveat.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states the specific resource (Lovelace dashboards) and the three modes: list, read one config, or search cards. It explicitly distinguishes itself from sibling tools (ha_get_dashboard_screenshot for renders, ha_search for entity discovery), so an agent can select it 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-not-to-use rules ('Not for a render alone', 'Not for entity discovery') and names the alternatives. It then explains exactly how parameters select each mode, including list_only, search criteria, and default read behavior, with concrete examples.
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 LabelARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| label_id | No | ID of the label to retrieve. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description adds genuinely useful behavior: the dual-mode list-vs-get semantics driven by parameter presence, which the 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Behavioral core is front-loaded, then examples, then cross-references. Every sentence serves a purpose, though the EXAMPLES block and cross-reference list make it longer than strictly minimal.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 here. For a single-parameter, fully documented read tool, the description covers mode selection, examples, and related sibling routing, leaving no gap an agent needs to fill.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 the schema's terse 'ID of the label to retrieve' by explaining the consequence of omitting it (returns all labels with configurations) versus supplying it (returns that label's config). It also supplies concrete example calls.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 explicitly distinguishes the two modes: listing all labels vs. retrieving one by ID. An agent can tell it apart from ha_config_set_label and the assignment 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the exact condition selecting each mode ('Without a label_id' vs 'With a label_id') and routes the agent to the correct alternatives: ha_config_set_label for create/update, and ha_set_entity/ha_set_device/ha_set_area_or_floor for assignment. 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 ScenesARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum scenes per page | |
| query | No | Filter scene names or IDs | |
| offset | No | Pagination offset | |
| scene_id | No | Scene 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_config | No | Also search full stored scene attribute values within a bounded scan |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description adds real behavior beyond them: listing returns only compact metadata, integration-managed scenes have no editable storage config or scene_id, and config-search results are bounded and explicitly not exhaustive. These are exactly the failure modes an agent would otherwise stumble into.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The mode split is front-loaded and the guidance, caveats, and examples are cleanly separated into paragraphs. It is denser than strictly necessary โ the parenthetical about python_transform updates could be tighter โ but every sentence carries routing or behavioral information rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 exposition is not required, and the description instead spends its budget on the things structure cannot express: the dual get/list mode, the integration-managed edge case, and the non-exhaustive search caveat. Nothing an agent needs to call this correctly or interpret its results is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 beats it by explaining the purpose of scene_id (retrieving the entities dict and config_hash for editing) and demonstrating search_in_config and query through concrete examples. It stops short of documenting limit/offset semantics, which stay with the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names the exact verb+resource pair and captures both modes ('Get a scene's complete configuration, or list and search scenes without scene_id'), which is a real distinction agents must make. It also explicitly separates itself from ha_search for cross-domain discovery, so the boundary with the most tempting sibling is drawn.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when to use this tool ('For ordinary scene discovery, use this tool'), when to use the alternative ('Use ha_search for cross-domain discovery and dependency searches'), and the downstream workflow (pass the returned scene_id and config_hash to ha_config_set_scene for python_transform updates). The closing pointer to ha_get_skill_guide covers the remaining escalation path.
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 ConfigARead-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 through the entity registry; 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
| Name | Required | Description | Default |
|---|---|---|---|
| script_id | Yes | Script identifier โ bare storage key ('morning_routine') or entity_id form ('script.morning_routine'); a leading 'script.' prefix is stripped before lookup. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so the safety profile is covered. The description goes beyond that with genuinely useful behavior: config_hash is stable across consecutive reads of an unchanged config (enabling change detection), and the returned script_id is canonicalized by the REST client with a documented fallback path. It omits error behavior when the script does not exist.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose is front-loaded and the examples are cheap and useful, but the paragraph on script_id resolution and prefix-handling mechanism is dense implementation detail for a lookup tool, and the trailing keyword list ("read inspect fetch view existing...") is pure noise. Roughly a third of the text could be cut without losing call-relevant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read tool with full annotation coverage and an output schema, the definition supplies everything needed to invoke it correctly: lookup forms, id normalization, and hash stability semantics. Only the failure mode (unknown script_id) is left unaddressed, which is a minor gap given the output schema exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema's script_id description already documents the bare-key and entity_id forms plus the stripping of a leading 'script.' prefix. The description's restatement and its two examples duplicate that schema content rather than adding new parameter meaning, so the baseline 3 applies; the canonicalization notes are about the return value, not the input.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource ("Get Home Assistant script configuration") and then enumerates what the config contains (sequence, mode, fields). It also distinguishes itself from ha_config_set_script, ha_config_remove_script, and ha_config_get_automation by name, so an agent can place it among the ~60 siblings 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The purpose is unambiguous read-only retrieval, the two call forms (bare key vs. entity_id) are demonstrated, and an explicit alternative is offered for a related need ("For detailed script configuration help, use ha_get_skill_guide"). It stops short of stating when NOT to use it, e.g. that ha_get_state should be used for runtime state rather than configuration.
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 ResourcesARead-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.
resource_mode is Core's lovelace resource mode; in 'yaml' mode
resources cannot be created, updated or deleted through the API.
EXAMPLES:
Next page: ha_config_list_dashboard_resources(offset=100)
List with full content: ha_config_list_dashboard_resources(include_content=True)
Verify an edit by hash: ha_config_list_dashboard_resources(include_hash=True)
Home Assistant 2026.6+ exposes resource management in the UI by default; API access works regardless of UI availability.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max resources to return per page | |
| offset | No | Number of resources to skip for pagination | |
| include_hash | No | Add "_config_hash" (SHA-256 hex digest of the decoded UTF-8 content) to each inline resource, e.g. to verify an edit or to pass as config_hash without fetching the content. | |
| include_content | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/idempotent annotations, it discloses non-obvious behavior: page-level vs aggregate counts (total_count/has_more vs inline_count/by_type), a token-saving preview mode for inline resources, a per-response content budget that yields '_content_truncated', and the constraint that in 'yaml' resource_mode mutations are impossible via the API.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The pagination/summary semantics are front-loaded and the examples are compact and actionable. The closing note about Home Assistant 2026.6+ UI availability is the least load-bearing sentence, though it does warn that UI availability and API availability differ.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only paginated list with a full output schema, the description covers everything an agent needs: paging semantics, when previews replace content, truncation signaling, ID availability for follow-up update/delete, and the yaml-mode restriction.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the base is 3; the description earns extra by tying parameters to intent โ offset for next page, include_content for full content, include_hash for verifying an edit or passing config_hash without refetching. It doesn't add new syntax, but it maps each flag to a concrete workflow.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a specific verb and resource ('List Lovelace dashboard resources') and scopes the content with examples ('custom cards, themes, CSS/JS'), which immediately separates it from siblings like ha_config_get_dashboard or ha_config_set_dashboard_resource.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It supplies concrete usage contexts (paging with offset=100, include_content=True for full content, include_hash=True to verify an edit) and points at ha_config_set_dashboard_resource for the create path. The one gap is that no explicit 'do not use this to...' exclusion is stated, so the agent must infer that mutations belong to the set/delete siblings.
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 GroupsARead-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 carries Core's state fields and attributes, its
members as entity_ids, and its object_id. auto is true for a group
created by ha_config_set_group (group.set) and false for a YAML group.
Core does not expose a group's all/any mode.
EXAMPLE: ha_config_list_groups(offset=100)
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max groups to return per page | |
| offset | No | Number of groups to skip for pagination |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered. The description adds genuinely non-obvious behavior: one page is returned while total_count/has_more report the full set, `auto` distinguishes service-created from YAML groups, and Core does not expose all/any mode โ a useful limitation disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, then scope exclusion, then return shape, then a one-line example โ logically ordered and free of filler. It is somewhat dense with detail (auto flag, all/any limitation) that could be trimmed, but each sentence carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only paginated list tool with an output schema and full annotation coverage, this covers scope, pagination, member representation, and a known Core limitation. Nothing an agent needs before calling is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both limit/offset are documented there, so the baseline is 3. The description adds value by explaining the pagination contract (one page returned, total_count/has_more signal the remainder) and gives a concrete offset example, tying the parameters to observable behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource (list Home Assistant entity groups with members) plus an explicit scope boundary: old-style group.set/YAML groups, not platform-specific light/cover groups. An agent can distinguish it from ha_config_set_group and from entity-level tools 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly frames when this applies (old-style groups created via group.set or YAML) and warns that platform-specific groups are separate entities, which is a real selection criterion. It stops short of naming a sibling tool for the platform-group case, so it is context-rich but not a full when-to-use/when-not matrix.
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 HelpersARead-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 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. A flow helper record carries entry_id and options instead of
id; a custom helper integration's options are withheld, marked
options_withheld.
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.
Listing flow types requires the ha_mcp_tools custom component (>= 1.1.0); without it, such a listing 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.
describe=True returns each field's name, type, required flag, options, default and (with helper_id) current value, or the menu_options a menu-based helper needs a menu_choice from. Call it before ha_config_set_helper to learn the config keys for a type.
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)
Fields for a template sensor: ha_config_list_helpers("template", describe=True, menu_choice="sensor")
For detailed helper documentation, use ha_get_skill_guide.
list all helpers input_boolean input_number input_text counter timer input_datetime input_select
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max helpers to return per page | |
| offset | No | Number of helpers to skip for pagination | |
| describe | No | Instead of listing, return the fields ha_config_set_helper accepts in config for helper_type, as Home Assistant reports them (the form the HA UI shows). | |
| helper_id | No | describe only: an existing helper (config entry id for flow helpers, entity_id or id otherwise); each field then carries its current value. | |
| helper_type | Yes | Helper type to list: a storage type (input_boolean, counter, timer, ...), a helper flow Home Assistant lists (template, group, utility_meter, ...), or 'all' for every type in one call. | |
| menu_choice | No | describe only: sub-type of a menu-based helper (template, group), e.g. 'sensor'. A choice that finishes the flow saves the helper: flow_type create_entry. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the read-only, idempotent safety profile, yet the description adds substantial behavior beyond them: pagination semantics (total_count/has_more), the exact per-record fields, the id vs entity_id resolution distinction and which one ha_config_set_helper resolves against, the degraded-registry case where only id/name survive plus a warning, and COMPONENT_NOT_INSTALLED failure modes for flow types and 'all'. It also discloses scope boundaries (storage-backed vs YAML-defined) and the person exception.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loading is good โ purpose, then record shape, then edge cases, then examples. But the body is unusually long and partly redundant, and it ends with an unpunctuated keyword fragment ('list all helpers input_boolean input_number ...') that reads as scraped trailing text rather than guidance, weakening the structure at the exact place an agent expects a clean closing instruction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter listing tool with an output schema, the description is more than sufficient: it covers scope, error modes, degraded behavior, cross-tool dependencies, and worked examples. Nothing an agent needs to invoke it or interpret the result is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 on top: it explains each helper_type class (storage type, listed flow type, 'all'), states that 'all' requires the custom component rather than returning a partial list, and clarifies that describe with helper_id attaches current values. The examples also show offset and menu_choice usage in context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource: 'List Home Assistant helpers of a specific type with their configurations.' It also names the companion tool (ha_config_set_helper) whose lookup relies on this one, so the agent can separate read-listing from mutation 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear when-to-use guidance in two places: 'Call it before ha_config_set_helper to learn the config keys for a type' for describe mode, and 'For detailed helper documentation, use ha_get_skill_guide' as the alternative for docs. It also states the preconditions (ha_mcp_tools component >= 1.1.0) that decide whether a call succeeds. It does not, however, address when to prefer this over generic entity reads such as ha_get_entity.
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 AutomationADestructiveIdempotent
Delete a Home Assistant automation permanently.
A disabled automation is removed too. The returned automation_id is
the resolved entity_id, falling back to the input.
EXAMPLES:
Delete automation: ha_config_remove_automation("automation.old_automation")
Delete by config id: ha_config_remove_automation("1712345678901")
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Wait for automation to be fully removed before returning. | |
| entity_id | Yes | The automation's entity_id (e.g. 'automation.old_automation') or its config id |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=true and readOnlyHint=false, so the safety profile is covered. The description adds real value beyond them: disabled automations are also deleted, and the response returns the resolved entity_id with a documented fallback. The Claude Desktop timeout workaround is operational context but is client-specific noise rather than general behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and examples are front-loaded and tight, but the final Claude Desktop troubleshooting paragraph consumes roughly a third of the text and does nothing to help an agent select or correctly invoke this tool. It distracts from the core definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema, full annotation coverage and 100% schema description coverage, almost all an agent needs is present, including the permanent-deletion and disabled-automation semantics. The only shortfall is the mismatched emphasis on a specific desktop client's timeout quirk rather than general failure handling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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, including the entity_id-or-config-id duality and the meaning of 'wait'. The description echoes the dual-id acceptance but adds no new syntax or format detail, making 3 the appropriate baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states a specific verb and resource ('Delete a Home Assistant automation permanently'), and the follow-up clarifies that disabled automations are also removed. An agent can distinguish this from ha_config_set_automation or ha_remove_entity 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Two concrete examples show the two accepted invocation styles (entity_id vs. config id), giving clear usage context. It stops short of naming when to prefer this over a generic sibling like ha_remove_entity, so no explicit exclusions are provided.
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 EventADestructiveIdempotent
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| uid | Yes | Unique identifier of the event to delete | |
| entity_id | Yes | Calendar entity ID (e.g., 'calendar.family') | |
| recurrence_id | No | Recurrence ID for recurring events | |
| recurrence_range | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructive/idempotent/readOnly=false, but the description adds real operational context beyond them: the fact that delete lives only on the WebSocket API (no REST fallback), that recurrence_range is matched verbatim, and a known Claude Desktop timeout/hang workaround. These are exactly the traits 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the purpose, then implementation rationale, examples, and the desktop workaround in a logical order. The implementation explanation is slightly long, but each section earns its place by preventing misuse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the API path, prerequisite for obtaining the uid, examples per use case, recurring-event semantics, and a known failure mode. With an output schema present, return-value explanation is unnecessary, leaving nothing material missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 meaning by showing how uid pairs with recurrence_id and recurrence_range in real calls and where the uid comes from. It does not add much beyond the schema for recurrence_range, which the schema already documents as verbatim.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Delete an event from a calendar') and clarifies the exact command path (WebSocket calendar/event/delete). It also distinguishes itself from siblings by pointing at ha_config_get_calendar_events() as the source of the UID, so an agent can tell it apart from the get/set calendar tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear prerequisite (obtain the uid from ha_config_get_calendar_events) and concrete examples for both single-event and recurring-range deletion. It does not explicitly state when NOT to use it, but the usage path 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_remove_categoryRemove CategoryADestructiveIdempotent
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| scope | Yes | Domain scope for the category (e.g., 'automation', 'script', 'scene', 'helpers'). | |
| category_id | Yes | ID of the category to delete |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by disclosing the cascading effect (removes the category assignment from all entities in that scope) and irreversibility, which the destructiveHint flag alone does not convey. It also flags a Claude Desktop hang workaround, adding operational context the structured fields 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and warning are front-loaded and efficient, but the closing Claude Desktop timeout paragraph is disproportionate and reads like internal debugging notes rather than tool-selection guidance, diluting the core message.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema covering return values and annotations covering the destructive/idempotent profile, the description fills the remaining gaps: cascade behavior, irreversibility, ID discovery, and a worked example. An agent has everything needed to call it safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters are documented in-schema, so the baseline is 3. The example clarifies the positional form and typical scope value ('automation'), but adds no constraints or edge cases beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 category') and narrows the scope to the category registry, which cleanly separates it from siblings like ha_config_remove_label, ha_config_remove_scene, and ha_config_set_category.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete invocation example and explicitly directs the agent to ha_config_get_category() to discover the required category_id. It lacks explicit 'do not use when...' or alternative-routing guidance, but the usage context 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_remove_groupRemove GroupADestructiveIdempotent
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 come back on group.reload or a 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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Wait for group to be fully removed before returning. | |
| object_id | Yes | Group identifier: a lowercase slug without the 'group.' prefix (e.g., 'living_room_lights') |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=true, yet the description adds genuinely new consequences: automations referencing the group may fail, YAML-defined groups reappear after group.reload or restart, and platform-specific groups are out of scope. The Claude Desktop timeout paragraph adds real operational context but is client-specific and reads as noise relative to the core call contract.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the bold when/when-not/warning sections make scanning easy. The final Claude Desktop troubleshooting paragraph is three sentences of client-specific workaround that inflate the definition without helping tool selection.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, 2-parameter tool with an output schema and rich annotations, the description covers everything an agent needs: scope limits, side effects, reversibility caveats, and where to find valid IDs. The presence of an output schema means return values need not be explained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, with object_id's slug format and the wait flag both fully documented in the schema. The description only reinforces object_id via the example ('living_room_lights') and never mentions `wait`, so it does not add meaning beyond the structured fields โ baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Remove a service-based Home Assistant entity group via the group.remove service') and immediately scopes it to old-style/YAML groups versus config-entry-backed ones. An agent can distinguish it from ha_remove_helpers_integrations and ha_config_set_group 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides both a 'When NOT to use' branch with the correct alternative tool and a 'When to use' branch describing which groups this tool can reach. It also names ha_config_list_groups for discovery and gives a concrete example call.
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 LabelADestructiveIdempotent
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| label_id | Yes | ID of the label to delete |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (destructive=true, readOnly=false, idempotent=true), the description discloses the cascading effect ('removes the label from all entities, devices, and areas') and irreversibility, which the annotations alone do not convey. The Claude Desktop timeout note adds operational context, though it is client-specific rather than tool-semantic.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose and warning are front-loaded effectively, but the long Claude Desktop workaround paragraph is verbose and tangential to the tool's own semantics, diluting the otherwise tight structure.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 the safety profile, the description only needs to add cascade behavior and irreversibility, which it does. Nothing essential for a correct call is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% so the schema already documents label_id, giving a baseline of 3. The example call showing the ID argument in use adds a small amount of value beyond the schema, warranting a slight bump.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 label') and reinforces it with 'Removes the label from the label registry'. This cleanly separates it from siblings like ha_config_set_label and ha_config_get_label.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Points the agent to the right companion tool ('Use ha_config_get_label() to find label IDs') and provides a concrete invocation example. It gives clear context but does not state explicit when-not-to-use conditions or compare against other removal tools.
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 SceneADestructiveIdempotent
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Wait for scene to be fully removed before returning. | |
| scene_id | Yes | Scene identifier to delete (e.g., 'old_scene') |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare destructiveHint=true and idempotentHint=true, so the mutation profile is covered. The description does add real value beyond that: the 405 failure mode for YAML scenes and the automation-breakage warning. However, the annotation set already carries the core safety signal, so the incremental disclosure is moderate rather than decisive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose, example, error case and warning are front-loaded and tight. The closing Claude Desktop timeout/hang paragraph is client-specific and noticeably longer than the rest, but it does carry actionable operational content, so it isn't padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive config mutation with an output schema present, this covers the failure mode (405 on YAML scenes), preconditions (UI-created only), the side-effect risk, and a recovery path on timeout. Nothing needed 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so scene_id and the wait flag are already documented in the schema itself. The description only echoes scene_id in the example and says nothing about wait, which is the baseline-3 case where the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a precise verb+resource ("Delete a Home Assistant scene") and immediately bounds the scope: only UI-created scenes are deletable. That scope statement cleanly separates it from the get/set scene siblings and from remove_automation/remove_script.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when-not to use it (YAML-defined scenes in scenes.yaml or configuration.yaml return 405) and names the alternative action (edit the configuration file directly). It also flags the downstream consequence of deleting a scene referenced via scene.turn_on.
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 ScriptADestructiveIdempotent
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Wait for script to be fully removed before returning. | |
| script_id | Yes | Script 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
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, readOnlyHint=false and idempotentHint=true, but the description adds failure modes the annotations cannot convey: the 405 for YAML scripts, the risk that dependent automations break, and a client-specific timeout/hang workaround. 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The headline, example, constraint paragraph and warning are front-loaded and each earns its place. The closing Claude Desktop workaround paragraph is long and reads as client-specific troubleshooting rather than tool-selection guidance, slightly diluting an otherwise tight entry.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations covering the safety profile and an output schema present, the description need not explain return values. It covers restrictions, risk and example usage, leaving only minor gaps around the wait parameter's practical effect.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both script_id (bare key vs entity_id, prefix stripping) and wait are already documented in the schema. The description's example reinforces the id form but adds no syntax or semantics beyond what the schema states; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 supplies a concrete example call. An agent can distinguish this from the sibling ha_config_set_script / ha_config_get_script 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly defines the negative condition: only UI-created scripts can be deleted, while YAML-defined scripts return 405 and must be edited in the config file instead. That is a real usability boundary with an alternative path, not just implied context.
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 AutomationADestructive
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. triggers-and-conditions.md, automation-actions.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
entity_id turns the automation on or off without touching config):
python_transform: surgical edits to an existing automation. Requires entity_id and config_hash from ha_config_get_automation(). Operates on the stored config exactly as ha_config_get_automation returns it, e.g. python_transform="config['triggers'].append({'trigger': 'state', 'entity_id': 'binary_sensor.motion', 'to': 'on'})"
config: new automations or full restructures. Regular automations have triggers and actions; blueprint automations have use_blueprint {path, input}. Home Assistant validates the config, and a rejection carries its own error text.
take_control_of_blueprint: convert a blueprint-backed automation into a standalone one. Takes no config of its own.
IDENTITY: omit entity_id and config['id'] to create with a generated ID.
Reusing an entity_id 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. operation says whether the write created or updated it.
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(entity_id="automation.x"); ha_config_set_automation(entity_id="automation.x", config_hash=current["config_hash"], config={...})
Disable: ha_config_set_automation(entity_id="automation.x", enabled=False)
Run now: ha_config_set_automation(entity_id="automation.x", run_actions=True)
Take control: ha_config_set_automation(entity_id="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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
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
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Wait for automation to be queryable before returning. Set to False for bulk operations. | |
| config | No | Complete automation configuration, written as given. Purpose-specific triggers/conditions (HA 2026.7+ default: 'trigger': '<domain>.<name>' with 'target'/'options') are valid config. Mutually exclusive with python_transform. | |
| enabled | No | 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. | |
| category | No | Category 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. | |
| entity_id | No | The automation's entity_id (automation.x) or its config id; omit to create. An unused config id creates the automation under that id; values such as 'new' are literal ids, not placeholders. | |
| config_hash | No | 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). | |
| run_actions | No | 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 entity_id and no config. | |
| MandatoryBPS | No | ||
| BestPracticeKey | No | Read-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_transform | No | 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" 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, index, count, sort, reverse - Dict methods: update, get, setdefault, keys, values, items, pop, clear - 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, lstrip, rstrip, removeprefix, removesuffix, split, join, replace, count, index - 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_blueprint | No | 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 entity_id; mutually exclusive with config and python_transform. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare destructiveHint=true and idempotentHint=false; the description goes far beyond them by disclosing that HA validates the config and returns its own error text, that a changed alias without config_hash is rejected before writing, that best_practice_warnings must be fixed before resubmitting, and that take-control is one-way and does not release the blueprint's user count. It also names the response fields ('operation', 'took_control_of_blueprint'), so the agent knows what to check after a write.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the MODES/IDENTITY/EXAMPLES structure is easy to scan, but the block is bloated: six worked examples plus a lengthy Claude Desktop timeout workaround paragraph that is client-specific troubleshooting rather than tool-selection guidance. Several points (mutual exclusivity of config and python_transform) are stated both in the description and the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter destructive write tool with an output schema and annotations, this covers the essential ground: identity/creation semantics, mode preconditions, validation feedback, and take-control consequences. Gaps are minor โ `category` and `wait` are never discussed in the description, and the client-specific timeout advice is noise relative to those omissions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 91%, so the baseline is 3, but the description earns above baseline by explaining parameter interactions the schema states only in isolation: entity_id alone with enabled toggles state without a config write, run_actions can be used standalone, and the three modes are mutually exclusive. It still leaves `category`, `wait`, and `MandatoryBPS` largely to their schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Create or update a Home Assistant automation') and immediately differentiates itself from siblings by pointing at ha_config_set_scene and ha_config_set_helper for adjacent use cases. The three named modes make the scope of the write operation unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: dedicated tools first for snapshots, template-derived values, and counters/timers/schedules/booleans; then mode selection (python_transform for surgical edits, config for new/full restructures, take_control_of_blueprint for de-bluerpinting), each with prerequisites. It also states the preconditions for each path (entity_id + config_hash for python_transform, read-before-rename rule), which is exactly the when/when-not guidance this dimension rewards.
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 EventADestructive
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
uidof an event to update, useha_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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| end | Yes | Event 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. | |
| uid | No | UID of an existing event to update. Omit to create a new event. | |
| rrule | No | RFC 5545 recurrence rule, without 'RRULE:' prefix (e.g., 'FREQ=WEEKLY;BYDAY=MO' or 'FREQ=MONTHLY;BYDAY=3SA'). Creates a recurring event series. | |
| start | Yes | Event start date or datetime in ISO format | |
| summary | Yes | Event title/summary | |
| location | No | Event location | |
| entity_id | Yes | Calendar entity ID (e.g., 'calendar.family') | |
| description | No | Event description | |
| recurrence_id | No | Only meaningful with 'uid': identifies one occurrence of a recurring series to update. | |
| recurrence_range | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructive=true and idempotent=false, but the description adds substantial non-obvious behavior: updates replace the whole event (non-patch), un-supplied description/location are cleared, rrule survives an omitted update (HA cannot express 'no recurrence'), and integration support varies for create/recurrence/update. This is exactly the kind of consequence-level detail 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Despite its length it is well front-loaded and organized with headers and examples, and each section carries decision-relevant content. The trailing Claude Desktop timeout paragraph is loosely scoped operational advice that sits outside the tool's core contract, costing it a point.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 10 params, an output schema, and full annotation coverage, the description supplies everything else an agent needs: mode selection, mixing rules, replacement semantics, recurrence quirks, and integration support limitations. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, and the description exceeds it by explaining the all-day vs timed distinction, the date/datetime mixing rejection, and the interplay of uid/recurrence_id/recurrence_range for partial-series updates. It stops short of fully restating every parameter, hence 4 rather than 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (create/update calendar event) and then splits the behavior precisely: create via REST service, recurring series via WebSocket when rrule is present, update mode when uid is passed. It also explicitly names sibling tools it is not, so an agent can route correctly 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Includes an explicit 'When NOT to use' block routing retrievals to ha_config_get_calendar_events and deletions to ha_config_remove_calendar_event, plus a note that finding a uid requires the get tool. It also specifies when create vs update fires (uid presence, rrule presence), covering both alternatives and triggers.
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 CategoryADestructive
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| icon | No | Material Design Icon (e.g., 'mdi:tag', 'mdi:label') | |
| name | Yes | Display name for the category | |
| scope | Yes | Domain scope for the category (e.g., 'automation', 'script', 'scene', 'helpers'). | |
| category_id | No | Category ID for updates. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=false, so the safety profile is covered. The description adds genuinely non-schema context: the create-vs-update branch, and an unusually specific Claude Desktop approval-timeout workaround with recovery guidance. It stops short of stating what an update overwrites (e.g., replacing name/icon on an existing category), which would matter given destructiveHint=true.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and the create/update rule are front-loaded, and the worked examples are compact and directly useful. The closing Claude Desktop timeout paragraph is long relative to its narrow applicability, but it is clearly set apart and does not bury the primary instruction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 mode selection, scope semantics, and the assignment follow-up. Minor gaps remain around valid scope values beyond the examples and what an update replaces, neither of which is fatal 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.
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 explaining the operational semantics of category_id โ omitting it creates, supplying it updates โ where the schema only says 'Category ID for updates.' Examples also demonstrate scope and icon usage in context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 (Home Assistant category), and explicitly names the conditional that selects the mode: omitted category_id means create, present means update. It further distinguishes categories from the sibling concept labels ('unlike labels, categories are domain-scoped'), so an agent can route correctly 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear context for both modes and a concrete follow-up path ('use ha_set_entity(categories=...) to assign it'), which resolves the common 'what next' question. It contrasts categories with labels, an implicit when-to-use guide. It does not name ha_config_get_category or ha_config_remove_category as the read/delete alternatives, so routing for those operations 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_set_dashboardCreate or Update DashboardADestructive
Create or update a Home Assistant dashboard.
MUST call ha_get_skill_guide OR refer to your locally installed skills first.
dashboard-guide.md ships under skill_content by default. A card's
fields: ha_config_get_dashboard(card_type=..., describe=True).
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| icon | No | MDI icon name (e.g., 'mdi:home', 'mdi:cellphone'). Omit to store none (the sidebar then shows mdi:view-dashboard); '' clears a stored icon. | |
| patch | No | 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. | |
| title | No | Sidebar name; required when creating a dashboard | |
| config | No | Dashboard configuration with views and cards. Omit or set to None to create dashboard without initial config. Mutually exclusive with python_transform and patch. | |
| url_path | Yes | Dashboard URL path (e.g., 'my-dashboard'). Use 'default' or 'lovelace' for the default dashboard. Home Assistant requires a hyphen in new dashboard paths. | |
| view_path | No | With return_screenshot: stable Lovelace views[].path to render. | |
| config_hash | No | 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). | |
| MandatoryBPS | No | ||
| require_admin | No | Restrict dashboard to admin users only. For existing dashboards, only updated when explicitly provided. | |
| BestPracticeKey | No | Read-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_sidebar | No | Show dashboard in sidebar navigation. For existing dashboards, only updated when explicitly provided. | |
| python_transform | No | 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]" 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, index, count, sort, reverse - Dict methods: update, get, setdefault, keys, values, items, pop, clear - 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, lstrip, rstrip, removeprefix, removesuffix, split, join, replace, count, index - 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_screenshot | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true, openWorldHint=true and idempotentHint=false, but the description adds substantive behavior beyond them: config 'Replaces everything', patch/python_transform require a config_hash for optimistic locking, metadata updates are rejected when combined with patch, index shifting after delete/add requires chaining operations, and only storage-mode dashboards are handled. The Claude Desktop timeout/hang workaround is operational context not derivable from any structured field.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the purpose and then organized under labeled MODES, an EXAMPLE, and a storage-vs-YAML scope note, which makes a genuinely complex 13-parameter tool scannable. It is long, and the Claude Desktop hang paragraph plus the external docs URL are marginal relative to the core selection logic, keeping it short of the top mark.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, open-world, three-mode write tool with no output schema, the definition covers everything an agent needs before calling: which mode to pick, what gets replaced, hash requirements, metadata-only call rules, strategy-dashboard and YAML-mode limits, and post-write verification. The remaining detail lives in the already-rich schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 92%, so the schema already documents most parameters well; the description's added value is cross-parameter semantics, notably the mutual exclusivity of patch/config/python_transform, the config_hash requirement differing by mode, and the rule that title/icon/require_admin/show_in_sidebar must be sent in a separate call when using patch or python_transform. It does not restate obvious per-field meanings, but it also doesn't explain every parameter (e.g. view_path and return_screenshot are left to the schema).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening line 'Create or update a Home Assistant dashboard' names a specific verb pair and resource. The MODES section then decomposes the tool into its three operating shapes (patch, python_transform, config), which lets an agent distinguish it from siblings like ha_config_get_dashboard and ha_config_delete_dashboard 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Guidance is explicit and conditional: patch for 'known paths with literal values', python_transform for 'loops or pattern-based changes', config for 'new dashboards only, or a full restructure'. It also names alternatives and prerequisites (call ha_config_get_dashboard for paths, ha_search/ha_get_overview for entity IDs, ha_get_dashboard_screenshot for re-checks, UI 'Take Control' for strategy dashboards), and states exclusions such as move/copy being unsupported.
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 ResourceADestructive
Create or update a dashboard resource (inline code or external URL).
Provide exactly one of:
edits: Search/replace edits on an existing inline resource. Any failing edit (or a config_hash mismatch) aborts the whole call and saves nothing. Prefer this over content= for small changes to a large card: only the changed text is sent.
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; 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/... (/config/www/; Marketplace cards: /local/community/...), /hacsfiles/... (HACS cards) or https://... (CDN). Supports all types: 'module', 'js', 'css', 'html'.
EXAMPLES:
Inline: ha_config_set_dashboard_resource(content="class MyCard extends HTMLElement { ... } customElements.define('my-card', MyCard);", resource_type="module")
Store cards need no call: in storage mode HACS and the Marketplace register the resource on install (adding it again duplicates it); with YAML-mode resources, list it under lovelace: resources: in configuration.yaml instead.
Update: ha_config_set_dashboard_resource(url="/local/my-card-v2.js", resource_type="module", resource_id="abc123")
Patch: ha_config_set_dashboard_resource(resource_id="abc123", edits=[{"old": "color: red", "new": "color: blue"}], config_hash="<_config_hash from the list>")
Inline writes return the stored content's config_hash.
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | URL of the resource. | |
| edits | No | Search/replace edits for an existing inline resource, applied in order. Requires resource_id; resource_type is ignored and the stored type is kept. | |
| content | No | JavaScript or CSS code to host inline. | |
| config_hash | No | With edits: refuse the patch unless the stored content has this hash when read, just before the write (the list's _config_hash with include_hash=True, or a previous write response's config_hash). A preflight check only: a change made by another client after that read is not detected and can be overwritten. | |
| resource_id | No | Resource ID to update. If omitted, creates a new resource. Get IDs from ha_config_list_dashboard_resources() | |
| resource_type | No | Resource type, validated by Home Assistant: 'module' for ES6 modules (modern cards), 'js' for legacy JavaScript (older custom cards), 'css' for stylesheets (themes, global styles), 'html' for HTML imports | module |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the destructiveHint=true / idempotentHint=false annotations: it discloses that any failing edit or hash mismatch aborts the whole call and saves nothing, that data: URIs are deterministic and capped at ~128KB, that CSP-blocked resources still report success but never render, and that a hard refresh is required after adding. These are exactly the failure modes an agent cannot 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core rule and organized into scannable per-mode bullets, so structure is strong. The final Claude Desktop timeout paragraph is the one section that sprawls and is tangential to the tool's contract, keeping this from a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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, yet the description still notes that inline writes return the stored content's config_hash. For a destructive, non-idempotent write with six optional parameters, everything an agent needs โ mode selection, atomicity, limits, post-write behavior โ is covered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline would be 3, but the description adds real meaning: the mutual-exclusivity rule across url/content/edits, the type support matrix (content accepts only 'module' and 'css', url accepts all four types), and why config_hash matters (abort-on-mismatch, with the caveat that it is preflight-only). It also explains that resource_type is ignored for edits.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
"Create or update a dashboard resource (inline code or external URL)" gives a specific verb pair, the resource, and the two supported content modes. It is immediately 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states "Provide exactly one of" the three modes, tells the agent when to prefer edits over content ("small changes to a large card"), and gives an exclusion: store-managed HACS/Marketplace cards need no call because re-adding duplicates them. It also routes to url='/local/...' when a reverse proxy injects CSP โ a concrete when-not condition for the content path.
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 GroupADestructive
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. They survive group.reload but
not a restart.
Core creates group.<object_id> when it does not exist (members from entities or add_entities) and updates it otherwise. 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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| icon | No | Material Design Icon (e.g., 'mdi:lightbulb-group') | |
| name | No | Friendly display name; a new group without one is named after object_id | |
| wait | No | Wait for group to be queryable before returning. Set to False for bulk operations. | |
| all_on | No | If True, all entities must be on for group to be on. Core does not expose the mode, so it cannot be read back and a backup does not restore it. | |
| entities | No | List of entity IDs for the group. When updating, replaces all entities; an empty list empties the group, or creates it without members. | |
| object_id | Yes | Group identifier: a lowercase slug without the 'group.' prefix (e.g., 'living_room_lights') | |
| add_entities | No | Entities to add to the group's members | |
| remove_entities | No | Entities to remove from the group's members |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, it discloses important lifecycle behavior: groups are only removable via `ha_config_remove_group`, are not found by `ha_remove_helpers_integrations`, survive group.reload but not restart, and core creates or updates depending on existence. It also states mutual exclusivity among entities, add_entities, and remove_entities, plus a Claude Desktop timeout workaround.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well structured and front-loads the most important routing guidance: when not to use, then when to use. It is somewhat long, especially the Claude Desktop timeout workaround, but the length is largely justified by the tool's complex behavior and compatibility caveats.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's mutation behavior, 8 parameters, and existing output schema, the description is complete enough for correct invocation. It covers creation/update semantics, lifecycle and removal constraints, restart behavior, mutual exclusivity, and a client-specific failure mode.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents every parameter well. The description adds value beyond the schema by stating that entities, add_entities, and remove_entities are mutually exclusive and by providing concrete usage examples. It does not need to restate parameter formats, but the extra semantic clarification is helpful.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 explicitly distinguishes this from config-entry-backed helper groups and names the sibling tool that should normally be used instead.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit 'When NOT to use' and 'When to use' guidance, naming `ha_config_set_helper(helper_type="group", ...)` as the preferred alternative for typical group requests. It also explains the compatibility and registry-membership conditions that select this 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_helperCreate or Update HelperADestructive
Create or update Home Assistant helpers: storage types, every helper flow HA lists, and config subentries.
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 (pass config dict): 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): every helper flow
Home Assistant lists, read from it at call time โ template, group,
utility_meter, derivative, min_max, threshold, integration, statistics,
trend, ... and custom helper integrations. Create requires name; for
updates pass the existing entry_id as helper_id (options flows reject
the name key).
Do not create otp here although Home Assistant lists it: the user sets
it up in the HA UI, since its secret is a credential they enroll in an
authenticator app.
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
actionfalls back to thehelper_id-presence discriminator (SIMPLE/FLOW) or thesubentry_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(andmenu_optionsfor menu-rooted helpers liketemplate/groupwhen 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_idas a LIST of successive selections, consumed one per menu encounter.
EXAMPLES:
input_number: ha_config_set_helper(helper_type="input_number", name="Target", config={"min": 0, "max": 100, "step": 5})
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
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
| Name | Required | Description | Default |
|---|---|---|---|
| icon | No | Material Design Icon (e.g., 'mdi:bell'); '' or ' ' clears it, except a zone's stored icon (the icon in the zone's own config, as the Home Assistant UI stores it; ha_get_zone shows it), which cannot be removed. On create, a storage helper's icon goes into its stored config when Home Assistant's schema for the type has an icon field, otherwise (person, tag, a new zone, or without the ha_mcp_tools component) into the entity registry; an update always sets the registry icon, and the stored icon too where the schema has one. Restoring a storage helper's automatic backup also puts back its registry icon, unless the backup predates registry icons being recorded; a flow helper's backup holds no icon, so its restore does not set one | |
| name | No | 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=...). | |
| wait | No | Wait for helper entity to be queryable before returning. Set to False for bulk operations. | |
| action | No | 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. | |
| config | No | Type-specific fields. On update it is a patch: a field you omit keeps its current value. SIMPLE types take these keys: input_select: options (list, required), initial. input_number: min, max (required), step, unit_of_measurement, mode ('box'/'slider'), initial. input_text: min, max (length), mode ('text'/'password'), initial, unit_of_measurement, pattern (regex the value must match). input_datetime: has_date, has_time, initial. input_boolean: initial. counter: initial (starting value), minimum, maximum, step, restore (default true). timer: duration ('HH:MM:SS' or seconds), restore (default false). schedule: monday..sunday, each a list of {'from': 'HH:MM', 'to': 'HH:MM'} with optional 'data' dict of extra attributes. zone: latitude, longitude (both required), radius (meters, default 100), passive (won't trigger person state changes). person: user_id, device_trackers (device_tracker entity IDs), picture (URL). tag: tag_id (omit on create to auto-generate), description. On input_* types, `initial` โ even false/0 โ disables last-state restore and forces that value on every HA restart. ha_config_list_helpers(helper_type, describe=True) lists a type's fields. FLOW types and config_subentry take the flow's fields; 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. | |
| labels | No | Labels to categorize the helper | |
| area_id | No | Area ID for the helper; '' or ' ' clears it | |
| category | No | Category ID for this helper (ha_config_get_category(scope='helpers') lists them, ha_config_set_category() creates one); '' or ' ' clears it. | |
| entry_id | No | Parent config entry ID when helper_type='config_subentry'. Use ha_get_integration() to find entry IDs. | |
| helper_id | No | The helper's storage id ('my_button'), not the name of its entity, or its full entity_id ('input_button.my_button'). Omit to create a new helper. | |
| helper_type | Yes | Type of helper to create or update. | |
| subentry_id | No | Existing config subentry ID to reconfigure when helper_type='config_subentry'. | |
| MandatoryBPS | No | ||
| subentry_type | No | Integration-defined subentry type when helper_type='config_subentry'. | |
| BestPracticeKey | No | Read-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_advanced_options | No | When 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
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes far beyond the annotations (destructiveHint=true, idempotentHint=false): it states that UPDATE is a patch that preserves type-specific fields, that the omitted `action` falls back to a helper_id/subentry_id presence discriminator, that undeclared flow config keys are silently ignored, that validation errors return data_schema/menu_options enabling self-correction, and that repeated menus take `next_step_id` as a list. It even discloses an environment-specific hang/timeout behavior on Claude Desktop, which annotations could never convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded and sectioned (purpose โ prerequisite โ type families โ behavior โ examples), but bloated: the Claude Desktop timeout paragraph and the trailing keyword dump ('create update new add helper input_boolean...') do not earn their space and read as SEO filler. Roughly a third of the text could be cut with no loss of actionable signal.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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, the description covers everything an agent needs: the prerequisite skill call, the type taxonomy, create vs update discriminators, patch semantics on update, config-key expectations, flow self-correction via validation errors, and four concrete examples. With an output schema present, no return-value prose is required, so nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is already 94%, so the baseline is 3; the description adds real value on top by explaining the `action` fallback semantics, the create-vs-update discriminator per family, and the shape of `config` per type (e.g. input_datetime has_date/has_time, zone lat/long required) plus the `step_values` escape hatch. It slightly duplicates the schema's own type-by-type key list rather than adding new meaning, which keeps it off a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Create or update Home Assistant helpers') and immediately scopes it into three distinct families (storage/SIMPLE types, FLOW types read from HA at call time, and config subentries), which is exactly the taxonomy the schema's helper_type accepts. An agent can tell this apart from ha_config_list_helpers or ha_remove_helpers_integrations 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit prerequisite ('MUST call ha_get_skill_guide OR refer to your locally installed skills first'), an explicit exclusion ('Do not create otp here... user sets it up in the HA UI'), and per-family create/update rules (create needs name, update needs helper_id; flow updates use entry_id because options flows reject name). It stops short of naming read/delete siblings as alternatives, so the routing guidance is context-rich but not fully comparative.
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 LabelADestructive
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| icon | No | Material Design Icon (e.g., 'mdi:tag', 'mdi:label') | |
| name | Yes | Display name for the label | |
| areas | No | 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=[]). | |
| color | No | Color for the label (e.g., 'red', 'blue', 'green', or hex like '#FF5733') | |
| label_id | No | Label ID for updates. | |
| description | No | Description of the label's purpose |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=false and openWorldHint=false, so the safety profile is carried. The description still adds real behavioral context beyond that: the Claude Desktop timeout/hang workaround, and the subtlety that area assignment is additive rather than replacing. It does not, however, spell out what an update does to fields not supplied.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core rule is front-loaded in the first sentence and examples are compact and scannable. The closing Claude Desktop paragraph is long and interrupts flow, but it carries non-obvious operational information rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 what is left: create/update semantics, field examples, assignment follow-ups, and a deployment-specific failure mode. 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.
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 genuine meaning: label_id's presence is the create-vs-update switch, plus three worked examples showing name/color/icon/description/areas combinations. That is more than the schema's terse per-field strings convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (create/update) and resource (Home Assistant label), and the branching rule is explicit: creates when label_id is absent, updates when it is present. It also characterizes labels as a cross-domain tagging system for entities, devices, and areas, distinguishing it from siblings like ha_config_set_category or ha_config_set_group.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use guidance (create vs update driven by label_id) and names the follow-up tools for applying the label to entities, devices, and areas. It also routes the agent elsewhere for clearing an area's labels (ha_set_area_or_floor with labels=[]), so the exclusion path is covered too.
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 SceneADestructive
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
create update modify edit scene entities snapshot activate apply turn on
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Wait for scene to be queryable before returning. Set to False for bulk operations. | |
| config | No | Scene 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. | |
| activate | No | 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. | |
| category | No | Category 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_id | Yes | Scene identifier (e.g., 'movie_night') | |
| config_hash | No | Config 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). | |
| MandatoryBPS | No | ||
| BestPracticeKey | No | Read-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_transform | No | 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']" 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, index, count, sort, reverse - Dict methods: update, get, setdefault, keys, values, items, pop, clear - 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, lstrip, rstrip, removeprefix, removesuffix, split, join, replace, count, index - 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
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=false, but the description adds real behavior: a mandatory skill-guide prerequisite, the fact that a write triggers a Home Assistant scene reload before activate runs, python_transform's sandbox rules (no imports, no while loops, infinite loops hang the server), and a Claude Desktop timeout failure mode with a recovery step. The one gap is that it never says explicitly that a full config replacement discards existing entity entries not present in the new config, which is the main destructive risk given destructiveHint=true.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The WHEN TO USE / WHEN NOT TO USE / EXAMPLE structure is well front-loaded, but the description is bloated: the skill-content and Claude Desktop workaround paragraphs sit in the middle of a tool definition, and it ends with raw keyword stuffing ('create update modify edit scene entities snapshot activate apply turn on'). Several sentences restate what the field descriptions already say verbatim, so trimming would cost nothing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter destructive mutation tool with an output schema present (so return values need not be explained), the description covers mode selection, activation, the skill-read protocol, and transform safety. What is missing is failure semantics: what happens on a config_hash mismatch, and what an agent should do when a full replacement is rejected or partially applied.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is already 89%, so the baseline is 3, but the description adds meaning the schema does not: the scene-vs-automation shape contrast ('scenes capture a snapshot of states as a dict', entities keyed by entity_id, not a list) plus a worked config example, and the note that activate alone works even on integration/YAML scenes. The python_transform examples further anchor the field's intent, though the long ALLOWED/FORBIDDEN inventory mostly duplicates the field description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening line states a specific verb pair and resource ('Create or update a Home Assistant scene'), and the following sections break the tool into three distinct modes (config replacement, python_transform, activate-only). It also names ha_config_get_scene and ha_config_remove_scene as the alternatives, so an agent can separate this tool from its siblings 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN TO USE bullets map each mode to a concrete situation ('surgical edits to an existing scene' vs 'creating a new scene, or wholesale replacement'), and a WHEN NOT TO USE bullet redirects listing/lookup to ha_config_get_scene. A callable example for activate-only is given, leaving no inference required.
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 ScriptADestructive
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-actions.md, triggers-and-conditions.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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
create update modify edit script sequence actions new script write save take control blueprint detach unlink standalone convert run start stop execute
| Name | Required | Description | Default |
|---|---|---|---|
| run | No | 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. | |
| wait | No | Wait for script to be queryable before returning. Set to False for bulk operations. | |
| config | No | Script configuration dictionary. Mutually exclusive with python_transform. | |
| category | No | Category 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_id | Yes | Script identifier โ bare storage key ('morning_routine') or entity_id form ('script.morning_routine'); a leading 'script.' prefix is stripped before lookup. | |
| variables | No | With run='start': values for the script's fields, passed to the run as its variables. | |
| config_hash | No | Config hash from ha_config_get_script for optimistic locking. Required for python_transform; optional for config updates (validates before full replacement if provided). | |
| MandatoryBPS | No | ||
| BestPracticeKey | No | Read-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_transform | No | 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" 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, index, count, sort, reverse - Dict methods: update, get, setdefault, keys, values, items, pop, clear - 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, lstrip, rstrip, removeprefix, removesuffix, split, join, replace, count, index - 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_blueprint | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (destructiveHint, non-idempotent): take control is one-way and does not free the blueprint (HA still counts the script as a user, so deletion stays refused), the script reload signals no completion, python_transform expressions with infinite loops hang the server, and config_hash provides optimistic locking. The documented 'wait' semantics and the known Claude Desktop hang workaround add further operational context the schema hints at but does not fully explain.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded and organized with a MODES block and EXAMPLES, but it carries avoidable weight: the appended keyword dump ('create update modify edit script sequence actions new script write save...') earns nothing, and the Claude Desktop timeout paragraph is verbose relative to its payoff. Substantively dense for an 11-parameter tool, so length is partly justified, but trimming is possible.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter, multi-mode, destructive write tool with an output schema, this covers what matters: mode selection, required inputs per mode, irreversibility of take-control, reload timing, and best-practice warning reporting under best_practice_warnings. Nothing an agent needs in order to call it safely appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 91%, so the baseline is 3; the description still adds real value by explaining cross-parameter constraints the per-property docs cannot โ which parameters are mutually exclusive, that python_transform requires config_hash, that run is used alone, and worked examples of config/use_blueprint/take_control payloads. It does not cover wait, category, or BestPracticeKey, leaving those entirely to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Create or update a Home Assistant script') and immediately draws the boundary against the nearest sibling: scripts use 'sequence', and trigger-based execution belongs to ha_config_set_automation. An agent can route between the two script/automation tools 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Enumerates four mutually exclusive modes with the condition that selects each (python_transform for surgical edits with config_hash; config for new/restructured; take_control for blueprint conversion; run alone with script_id), and names alternatives โ ha_config_get_script for hashes, ha_manage_blueprints(substitute) for previewing without writing. It also excludes: 'write config changes in a separate call first'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_eval_templateEvaluate TemplateARead-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})
| Name | Required | Description | Default |
|---|---|---|---|
| strict | No | Template only: fail on undefined variables instead of rendering them as empty (reported in warnings when report_errors is on) | |
| timeout | No | Template only: maximum render time in seconds | |
| template | No | Jinja2 template to render. Omit when using 'condition'. | |
| condition | No | 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': [...]}. | |
| variables | No | Variables available to the template or condition, e.g. sample trigger data: {'trigger': {'to_state': {'state': 'on'}}} | |
| report_errors | No | Template only: have Home Assistant report render errors and warnings. With false, a failed render is only written to Home Assistant's log. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety burden is covered. The description still adds real behavioral context: warnings on suspect renders, strict turning them into hard errors, failed renders returning HA error text plus line/source_line, and the condition-testing mode. Some of this (warnings, strict) overlaps the schema, but the return-on-partial-failure behavior is genuine added value.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then labeled sections (When to use / When NOT to use / Results / EXAMPLES) make it scannable. Every sentence carries information; nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return format need not be explained, and the description covers the remaining gaps: two operating modes, error/warning behavior, timeout/strict semantics via schema, and cross-references to sibling tools. 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.
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 examples go beyond the schema by showing concrete usage of template, variables (with sample trigger data) and condition (including the 'and' combinator), which clarifies how the parameters interrelate and that template/condition are mutually exclusive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (execute/render) and two concrete resources (Jinja2 template, automation condition) in Home Assistant, and explicitly contrasts with the plain-value siblings ha_get_state / ha_search. An agent can distinguish this from every sibling 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Contains explicit 'When to use' and 'When NOT to use' sections, names the alternatives (ha_get_state, ha_search, native constructs, ha_get_skill_guide for anti-patterns) and gives the heuristic 'one render beats fetching N states'. This is the full when/when-not/alternatives pattern.
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)ARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | 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. | |
| query | No | App (add-on) name/description filter (only for source='available') | |
| source | No | 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. | |
| repository | No | Filter by repository slug, e.g., 'core', 'community' (only for source='available') | |
| include_stats | No | Include CPU/memory usage statistics (only for source='installed') |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, non-destructive, idempotent, and open-world. The description adds the meaningful prerequisite that this requires Home Assistant OS or Supervised, which is not in the annotations. Output schema covers return values, so no further disclosure is required.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core purpose, then the exclusion, then mode selection and the environment prerequisite. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only, multi-mode list/detail tool with full schema description coverage and an output schema, the description covers purpose, exclusions, mode selection, and the OS/Supervised requirement. Nothing an agent needs is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents parameter meaning richly (including slug prefix variability and the version vs version_installed nuance). The description restates the slug/source usage but adds no semantics beyond the schema, so baseline 3 is correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 apps/add-ons) with clear scope: installed/available inventory or single-app details. Explicitly distinguishes from the mutating sibling ha_manage_app.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use for each mode (slug for details, source='installed' for inventory, source='available' for store discovery) and a when-not-to-use clause routing state/config changes to ha_manage_app.
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 TracesARead-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:
List recent traces (omit run_id): returns a summary of recent runs with timestamps, triggers, and status. Use
offsetto page deeper whenhas_moreis true. ha_get_automation_traces("automation.motion_light")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")
Add logbook entries and context metadata: detailed=True. Script-style action paths (sequence/, numeric) are always matched regardless of this flag.
Variables at every step (trigger sets and null entries are still omitted): deduplicate=False.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of traces to return when listing (default: 10, max: 50). | |
| order | No | Order traces are returned in. 'newest' returns most-recent first; 'oldest' returns chronological-first. | newest |
| offset | No | Number of traces to skip from the start of the requested order. Use with `limit` to page through stored traces when `total_available > limit`. | |
| run_id | No | Specific trace run_id to retrieve detailed trace. | |
| detailed | No | Include extra diagnostic data: logbook entries and context metadata. | |
| sections | No | Comma-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'. | |
| deduplicate | No | 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. | |
| automation_id | Yes | Automation or script entity_id (e.g., 'automation.motion_light' or 'script.morning_routine') |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the read-only/idempotent safety profile, and the description adds real behavioral context on top: traces are retained only for the most recent runs per the stored_traces setting, older runs are unavailable, list mode is paginated via offset/has_more, and 'state' values are defined ('stopped', 'running', or error). It also discloses that trigger-set variables and null entries are omitted regardless of deduplicate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and the payload contents, then a scannable numbered mode list; length is justified by 8 parameters and three distinct call shapes. Minor redundancy: the deduplicate behavior is explained in both the description and the schema, and the retention note could sit closer to the list mode it constrains.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter, read-only diagnostic tool with an output schema present, the description covers the remaining agent-facing unknowns: what is returned in each mode, retention limits, pagination, and section/flag semantics. An agent can choose the right mode and interpret results without further guessing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so a 3 is the baseline, but the description adds operational meaning the schema lacks: worked examples of the automation_id and run_id formats, the offset-when-has_more paging rule, and the caveat that script-style action paths (sequence/, numeric) are always matched regardless of the detailed flag.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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') plus the goal ('to debug issues'), and enumerates exactly what a trace contains (trigger, condition results, action/branch, errors, variables). This is enough to separate it from config-oriented siblings like ha_config_get_automation and from log/history 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The numbered USAGE MODES give explicit conditional guidance: omit run_id to list vs. supply run_id for detail, use offset when has_more is true, detailed=True for logbook/context, deduplicate=False for per-step variables, with literal call examples. It never names an alternative tool (e.g., ha_get_logs or ha_get_history) for when traces are the wrong instrument, so it stops 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_get_camera_imageGet Camera ImageARead-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), in the format Home Assistant serves (JPEG, PNG, or GIF), alongside a short text block stating the served snapshot's size and retrieval time in Home Assistant local time.
Do not use it to inspect the camera's state or attributes โ use ha_search or ha_get_state for those; it returns an image, not state. Use it when you need to see the current scene, and on high-resolution cameras pass width and height to reduce token usage.
EXAMPLE: ha_get_camera_image(entity_id="camera.backyard", width=640, height=480)
| Name | Required | Description | Default |
|---|---|---|---|
| width | No | Width for the resized image. Home Assistant rescales only when both width and height are given and the camera serves JPEG. | |
| height | No | Height for the resized image. Home Assistant rescales only when both width and height are given and the camera serves JPEG. | |
| entity_id | Yes | Camera entity ID (e.g., 'camera.front_door') |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, not open-world), yet the description adds genuinely new behavioral detail: the return format (JPEG/PNG/GIF), that the image is returned directly, and that a short text block accompanies it with size and retrieval time in HA local time. It stops short of describing failure modes (e.g., camera offline) but adds real value 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first sentence, followed by return detail, exclusions, a parameter tip, and an example. Slightly long and a touch of framing prose ('for visual analysis'), but nearly every sentence carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description correctly explains what comes back (the image plus a size/timestamp text block) and the supported formats. For a single-required-param read tool, nothing an agent needs to invoke and interpret it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 the intent behind width/height (reduce token usage on high-resolution cameras) and provides a concrete call example. This complements rather than repeats the schema's own descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb (Get) plus resource (a camera entity's snapshot image) with the exact return described. It explicitly differentiates itself from siblings: 'use ha_search or ha_get_state for those; it returns an image, not state.' An agent can tell it apart from every other ha_ tool 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-to-use scenarios (security checks, delivery verification, confirming a garage door closed) and an explicit when-not-to-use rule routing state inspection to ha_search/ha_get_state. It even includes a conditional usage tip for high-resolution cameras (pass width/height to reduce token usage).
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)ARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max devices to return per page in list mode | |
| offset | No | Number of devices to skip for pagination | |
| area_id | No | Filter devices by area ID (e.g., 'living_room') | |
| device_id | No | Device ID to retrieve details for. | |
| entity_id | No | Entity ID to find the associated device for (e.g., 'light.living_room') | |
| integration | No | Filter devices by integration: 'zha', 'zigbee2mqtt', 'zwave_js', 'mqtt', 'hue', etc. | |
| detail_level | No | '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 |
| manufacturer | No | Filter devices by manufacturer name (e.g., 'Philips') |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered. The description adds genuinely useful behavioral context beyond that: detail_level interaction with list mode, the fact that single-device lookups always return full detail, and which protocol-specific fields (ieee_address, node_id/node_status, node_diagnostics) come back. It does not discuss pagination limits or ordering, but the remaining gap is minor.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded with the mode distinction before the example block, and the bolded headers make scanning easy. It is on the long side, but each example line illustrates a distinct parameter combination, so few lines are wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return-value documentation is not required, yet the description still notes protocol-specific return fields, and it covers both call modes, filtering, pagination, and the handoff to ha_manage_radio. Nothing needed 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so a baseline 3 applies, but the description goes further by showing how parameters combine (offset=50 for next page, limit=10 with detail_level='full', integration values mapped to protocols) and by clarifying that detail_level is effectively ignored in single-device mode โ semantics the schema does not state.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 the two operating modes (list with filters vs. single-device lookup). It also enumerates the protocol coverage (Zigbee/ZHA/Z2M, Z-Wave, Matter), which lets an agent distinguish it from generic entity-oriented siblings 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use guidance for both modes with concrete invocation examples (offset pagination, area/integration filtering, device_id vs entity_id), plus a routing hint to ha_manage_radio for Matter management. The alternatives and conditions are spelled out rather than inferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_get_entityGet EntityARead-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
| Name | Required | Description | Default |
|---|---|---|---|
| domain | No | Resolver filter (unique_id mode only): restrict matches to this entity domain, e.g. 'sensor'. | |
| platform | No | Resolver filter (unique_id mode only): restrict matches to this integration platform, e.g. 'hue'. | |
| entity_id | No | Entity ID or list of entity IDs to retrieve (e.g., 'sensor.temperature' or ['light.living_room', 'switch.porch']). | |
| unique_id | No | Stable integration unique_id (entity_id is mutable, unique_id is not). Mutually exclusive with entity_id. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the safe read-only/idempotent profile, yet the description adds substantial non-obvious behavior: resolver mode reads as_partial_dict so aliases and device_class override return defaults, the same unique_id can match multiple platforms, and enrichment keys (area/floor/label_names) appear only when the component advertises them. This is exactly the kind of context structured fields 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded and dense with genuinely useful detail, but it is overlong and ends with an unexplained, machine-sounding fragment ('get entity state attributes details single specific entity_id') that reads as keyword stuffing and should have been removed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Has an output schema and 100% param coverage, so return values and argument formats are already documented; the description still supplies the mode-switching logic, cross-tool handoff (ha_get_integration for config_entry_id), and conditional-field caveats. 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description goes beyond it: it explains that unique_id and entity_id are alternate modes, that domain/platform act as resolver-only filters, and that multiple matches are returned with a matches count. It does not repeat the scalar entity_id format syntax, which the schema already handles.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Get entity registry information for one or more entities') and immediately differentiates itself from the closest sibling by scoping to registry metadata rather than state. The RELATED TOOLS line makes the boundary against ha_get_state explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear routing context: use RESOLVER MODE with unique_id when resolving a stable integration id, and use ha_set_entity/ha_get_state/ha_search for adjacent jobs. It names alternatives but the when-not conditions are mostly implied by the RELATED TOOLS list rather than stated as exclusions.
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 ExposureARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| assistant | No | Filter by assistant: 'conversation', 'cloud.alexa', or 'cloud.google_assistant'. | |
| entity_id | No | Entity ID to check exposure settings for. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this a safe, idempotent read, and the description adds genuinely non-obvious semantics on top: an absent entity means defaults apply, has_custom_settings=False implies exposed_to is all False, and default values are deliberately not computed. It also discloses the conditional enrichment behavior tied to the ha_mcp_tools component advertising the exposure capability.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core list-vs-single distinction, then examples, then the enrichment caveat, so an agent gets the essentials in the first sentence. It is verbose in places (a full paragraph on enrichment fields) but each section carries distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given an output schema already documents the return structure, the description still explains the semantics of the returned fields (exposed_to, is_exposed_anywhere, has_custom_settings) and the list-mode summary/entity_info shape. Nothing needed to invoke or interpret the tool is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 beyond it by explaining that entity_id acts as a mode switch that changes the entire return shape, and by enumerating the assistant filter values. It adds useful meaning without documenting syntax the schema already covers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (get entity exposure settings) and immediately splits the behavior into two distinct modes keyed on the entity_id parameter. An agent can tell exactly what it returns in each case 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly describes when to call it without entity_id (list all entities with explicit settings) versus with entity_id (inspect a single entity), and notes that a second ha_search is unnecessary when the capability is advertised. It does not name sibling alternatives like ha_get_entity or ha_set_entity for comparison, so it stops short of full alternative routing.
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 InfoARead-onlyIdempotent
Get HACS (Home Assistant Community Store) data โ search the store or fetch repository details.
On Home Assistant 2026.11+ HACS is the built-in Marketplace; the tool
uses whichever the instance has. Use action="search" to
search/browse/list store repositories, or action="info" for one
repository's full details as the store reports them (the README is in
additional_info). 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")
Before an update: ha_get_hacs_info(action="info", repository_id="owner/repo", include_releases=True)
Caveats: info fetches full repository detail from GitHub, so it can hit GitHub
rate limits without a connected GitHub account; search reads the locally cached
repository index.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Search keyword (action='search') | |
| action | Yes | 'search' the store, or 'info' for one repository | |
| offset | No | Results to skip for pagination (action='search') | |
| category | No | Filter by category (action='search'). One of the categories the store reports (search's store.categories); 'lovelace' means 'plugin' (dashboard cards). | |
| max_results | No | Maximum number of results (action='search') | |
| repository_id | No | Numeric HACS ID or 'owner/repo' path (action='info') | |
| installed_only | No | Only return installed repositories (action='search') | |
| include_releases | No | Add the repository's GitHub releases and the release notes of versions newer than the installed one (action='info') |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, open-world behavior, but the description adds genuinely new operational context: `info` hits GitHub and can be rate-limited without a connected account, while `search` reads a local cache, and the README arrives in `additional_info`. It does not discuss pagination limits or what an empty result looks like, 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose statement is front-loaded, then caveats and examples are cleanly separated by bold headers. The examples partly restate what the schema's per-parameter descriptions already say, so the block is slightly longer than strictly needed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter, two-action tool with an output schema (so return values needn't be described), the description covers mode selection, mutation alternative, rate-limit risk, and a workflow tip. An agent has everything required to call it correctly on the first attempt.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 more by showing concrete parameter combinations in four worked examples (query+category, installed_only, repository_id in both numeric and owner/repo form, include_releases before an update). It does not add syntax or edge-case semantics beyond those examples.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names the specific verb and resource: retrieve HACS (Community Store) data with two modes, search or repository details. It explicitly contrasts with the sibling `ha_manage_hacs` (install/add) and with domain-specific tools, so an agent can distinguish 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states exactly when to use `action="search"` (browse/list) versus `action="info"` (one repo's full details), names the alternative tool for mutations, and adds a concrete routing tip for the dashboard-card case. No inference is left 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_historyGet Entity History or StatisticsARead-onlyIdempotent
Get historical data from Home Assistant's recorder.
WHEN NOT TO USE: For current values, use ha_get_state. To inspect Energy Dashboard preferences and configured statistics, use ha_manage_energy_prefs(mode="get", include_statistics=True).
WHEN TO USE: Use source="history" (default) to troubleshoot why a value changed, check event sequences, or analyze recent patterns. Use summary=True when the question needs a count or a duration ("was it ever unavailable?") rather than the rows. Use source="statistics" for long-term trends and period averages.
CAVEATS: History rows rename Core's compact keys once: s -> state, a -> attributes, lu -> last_updated, lc -> last_changed. History event times are ISO strings in HA's timezone (UTC if unavailable). When Core omits lc, last_changed falls back to last_updated; this may not be the actual state-change time with significant_changes_only (default) or a window-start snapshot. Attributes and unknown fields pass through unchanged. Statistics timestamps use Unix milliseconds. Minimal history may omit attributes. No duplicate compact aliases are added. include_schema=True retains core_contract even when fields selects other data keys.
Output units come from Core recorder metadata and reflect its display-unit conversion; unresolved units include a reason. statistics_metadata preserves Core's native fields.
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. With source="history", summary=True does not combine with offset > 0. 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")
Time per state: ha_get_history(entity_ids="binary_sensor.door", start_time="7d", summary=True)
ha_get_history(source="statistics", entity_ids="sensor.living_room_temperature", start_time="6m", period="month", statistic_types=["mean", "min", "max"])
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max entries per entity. Default: 100. For source="history": state rows; with summary=True, the states listed in summary.states, longest first. For source="statistics": aggregated rows. With multiple entity_ids, total rows returned can reach limit ร len(entity_ids). | |
| order | No | Sort order for history entries. "desc": newest first. "asc": oldest first. Ignored when source="statistics". | desc |
| fields | No | Return only the specified keys within data 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 and timezone metadata remain outside data and are always retained. | |
| offset | No | Number of entries to skip per entity for pagination. | |
| period | No | Aggregation period: "5minute", "hour", "day", "week", "month", "year". Ignored when source="history" | day |
| source | No | 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). | history |
| summary | No | Return per-entity totals instead of state rows: changes (switches to a different state; the state at the window start is not one, but without that state, e.g. with core_options {'include_start_time_state': false} or purged history, the first recorded state counts as one), first_change and last_change (null without changes), and per state its periods (separate spells) and duration_s (seconds, up to end_time or now, whichever is earlier). distinct_states counts every state, listed or not. Ignored when source="statistics". | |
| end_time | No | End time: same formats as start_time. Omit it to end at the current time | |
| entity_ids | Yes | Entity ID(s) to query. Can be a single ID, comma-separated string, or JSON array. | |
| start_time | No | Start time: ISO datetime or a look-back from now with unit min, h, d, w or m (= 30 days), e.g. '30min' = 30 minutes ago. Default: 24h ago for history, 30d ago for statistics | |
| core_options | No | Additional native Core request fields, e.g. {'units': {'energy': 'MWh'}} for statistics. Core validates them. Cannot override fields controlled by the tool's parameters or query limits. | |
| include_schema | No | Include the running Core's native request schema when the component supports discovery. | |
| statistic_types | No | Native Core statistics types (for example "sum", "change", "last_reset"). Default: Core chooses all. Use include_schema=True to discover the installed contract. Ignored when source="history" | |
| minimal_response | No | Return only states/timestamps without attributes. Ignored when source="statistics" | |
| significant_changes_only | No | Filter to significant state changes only. Ignored when source="statistics" |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), and the description adds genuinely new behavior: Core key renaming (s/a/lu/lc), ISO-with-HA-timezone timestamps, last_changed fallback to last_updated under significant_changes_only, statistics timestamps in Unix ms, and optional admin-enabled recorder workload guardrails that can reject oversized queries. These are operational facts no annotation or schema 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded with WHEN NOT TO USE first, then WHEN TO USE, CAVEATS, EXAMPLES โ a clean scannable structure with four concrete invocations. It is long, and a few caveat sentences are near-opaque filler for a caller ('No duplicate compact aliases are added', 'include_schema=True retains core_contract even when fields selects other data keys') that could be trimmed without loss.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 15-parameter tool with an output schema, the description supplies everything the schema cannot: source selection, retention asymmetry (history ~10 days vs permanent statistics requiring state_class), pagination protocol via has_more/next_offset, and guardrail behavior. Return-shape detail is correctly delegated to the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 clears that bar by documenting cross-parameter constraints the schema does not: limit/offset are client-side, offset must be 0 with multiple entity_ids, and summary=True cannot combine with offset>0 for history. It also ties source/summary/period to intent with worked examples. It stops short of a systematic per-parameter walkthrough, which is expected given the schema already does that work.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get historical data from Home Assistant's recorder') and immediately partitions the domain into source="history" vs source="statistics" with what each is for. It also names the sibling it is not (ha_get_state for current values) so an agent can route 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN NOT TO USE block naming ha_get_state and ha_manage_energy_prefs(mode="get", include_statistics=True), followed by WHEN TO USE rules keyed to intent (troubleshoot a change, need a count/duration, long-term trends). This is the full when/when-not/alternative triad.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_get_integrationGet IntegrationARead-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.
WHEN NOT TO USE:
Changing an entry, or describing a setup form or a form behind a menu: use ha_set_integration (without config it returns the form).
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")
Options flow that opens with a menu: ha_get_integration(entry_id="abc123", include_schema=True) lists menu_options; ha_set_integration(entry_id="abc123", menu_choice="") opens that form
List template entries with their options: ha_get_integration(domain="template")
Only service integrations: ha_get_integration(integration_type="service")
An entry's panel commands: ha_get_integration(entry_id="abc123", include_ws_commands=True)
OPTIONS: with the component, 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);
values that match a secrets.yaml entry are returned as
"**redacted**". Without it, include_schema=True reads options
from the options form: unset fields at their schema default, a
section's fields at the top level. With redact_secrets on, every
option value of an entry that has an options flow is redacted unless
include_schema=True reads the form that names the password fields;
an entry without an options flow has no such form, so its option
values come back as stored.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max entries to return per page in list mode | |
| query | No | When listing, search by domain or title. | |
| domain | No | Filter by integration domain (e.g. 'template', 'group'). When set, includes the full options/configuration for each entry. | |
| offset | No | Number of entries to skip for pagination | |
| entry_id | No | Config entry ID to get details for. | |
| device_id | No | 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. | |
| exact_match | No | Use exact substring matching for query filter. Set to False for fuzzy matching when the query may contain typos. | |
| subentry_id | No | Existing subentry ID used with include_subentry_schema=True to inspect a reconfigure flow. | |
| subentry_type | No | Integration-defined subentry type used with include_subentry_schema=True. | |
| include_schema | No | When entry_id is set, also describe the options flow form as options_schema.fields: each field's type, current value and Home Assistant's help text. A flow that opens with a menu lists menu_options instead (ha_set_integration(entry_id=..., menu_choice=...) opens one), and a flow Home Assistant aborts gives aborted, plus reason_text when Home Assistant has a string for the reason. Only applies when supports_options=true. The flow is started, described and aborted; starting it runs the integration's own flow code, and some integrations act there (Sonoff LAN signs in to its cloud). For an integration's setup form, use ha_set_integration(domain=...). | |
| include_options | No | Include 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 here. Read through the ha_mcp_tools custom component; without it an entry that has an options flow comes back without options and a warning, and only include_schema on that entry reads them. With the component, prefer this over include_schema when you only need the current values; use include_schema when you also need the field types or selector metadata. | |
| integration_type | No | When listing, keep only entries whose integration has this type, matched by Home Assistant itself (the manifest's integration_type, e.g. 'device', 'hub', 'service', 'helper'). Home Assistant does not reject an unknown type: for any type but 'helper' it also keeps entries whose integration it cannot find (e.g. a removed custom integration), so an unknown type returns only those. | |
| diagnostics_fields | No | 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. | |
| include_subentries | No | When entry_id is set, include config subentries for the integration entry. | |
| include_diagnostics | No | 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. | |
| include_ws_commands | No | When entry_id is set, also list the integration's own '<domain>/' WebSocket commands (the API its panel uses, e.g. KNX's knx/get_knx_project) under ws_commands, each with the request schema Home Assistant registered for it. Send one with ha_set_integration(entry_id=..., ws_command=...). Needs the ha_mcp_tools custom component; without it a warning says the list is unavailable. | |
| diagnostics_data_path | No | 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. | |
| show_advanced_options | No | When 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_limit | No | 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. | |
| diagnostics_data_offset | No | 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. | |
| include_subentry_schema | No | When entry_id is set, describe a config subentry flow's form as subentry_schema.fields (same shape as include_schema); the flow is aborted afterwards. Pair with subentry_type, and optionally subentry_id for the reconfigure form. | |
| diagnostics_truncate_at_bytes | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true and destructiveHint=false, yet the description still adds substantial non-obvious behavior: include_schema starts and aborts a real options flow, and "some integrations act there (Sonoff LAN signs in to its cloud)." It also discloses secrets redaction rules, that diagnostics payloads can reach several MB (Hue ~290 KB, ZHA/MQTT/ESPHome several MB), and that several features require the ha_mcp_tools custom component or degrade to a warning.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The front-loading and section labels (WHEN NOT TO USE, EXAMPLES, OPTIONS) are strong, but the closing paragraphs are dense unbroken prose about return-value semantics โ stored options mapping, log_level/log_level_raw sentinels โ that a separate output schema already carries. The log_level versus ha_get_app cross-comparison warning in particular is long for what it conveys.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 22-parameter tool with no required parameters, the definition covers the list/detail split, the heavy optional diagnostics path, custom-component prerequisites, redaction under redact_secrets, and the side effects of schema inspection. Combined with an existing output schema, an agent has everything needed to call it correctly on the first attempt.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 above that by explaining interactions the schema cannot: when include_options is auto-enabled (domain filter), why include_options is preferable to include_schema when only current values are needed, how diagnostics_data_path plus limit/offset swap data for a pagination envelope, and the integration_type quirk that unknown types silently match unfindable integrations. It stops short of a genuinely parameter-by-parameter guide, leaving some relationships to be inferred from examples.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ("Get integration (config entry) information") and immediately splits the two operating modes: without entry_id it lists, with entry_id it returns details including options. It explicitly names the sibling ha_set_integration as the mutation counterpart, so an agent can distinguish read from write 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Contains a labeled "WHEN NOT TO USE" block that routes entry changes and setup-form retrieval to ha_set_integration, and eight worked examples map concrete intents (search, password-field form, subentry reconfigure, diagnostics paging, service-only listing) onto exact parameter combinations. Both the when and the when-not are stated, which is the top of the scale.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_get_logsGet LogsARead-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 the full exception traceback, 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; structured=False returns the raw records instead, so it covers what falls outside system_log's WARNING+ in-memory buffer. Use structured=True 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.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | 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, multicast โ here 'supervisor' is the Supervisor service's own logs, not an app with that name. | |
| level | No | system / error_log only: keep only entries at exactly this level (ERROR, WARNING, INFO, DEBUG, CRITICAL); it is not a threshold. For source='error_log' it matches the record's header only, so matching text in a traceback frame does not satisfy it. | |
| limit | No | Max entries/lines to return. For source='error_log' with structured=False it bounds returned lines: whole records are selected within it, and a record longer than it is cut (header and last lines kept) with a warning naming how many lines were cut. Does not apply to source='error_log' with structured=True. | |
| order | No | 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; error_log orders whole records and keeps each record's lines in place. Ignored for source='logger', and for source='error_log' with structured=True. | newest |
| top_n | No | Max distinct issues to return when structured=True (default 20, capped at 500). | |
| offset | No | 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, or is the opaque token a previous page returned in 'next_offset' on Supervisor/journald installs, which keeps paging exact while new log lines arrive (`structured=True` accepts and returns that token too); 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. | |
| search | No | Keyword filter on entries/lines. For source='system' it matches message, name and exception text; for the raw error_log path it matches any line of a record, so a traceback frame brings back its header and siblings. Structured error_log mode matches message and logger name only; source='logger' matches the integration domain. | |
| source | No | 'logbook': entity state-change history. 'system': HA's structured system_log entries (errors, warnings) with full exception tracebacks; search also matches those exceptions. 'error_log': raw log text (home-assistant.log on container/pip installs; HA Core's journald stream on Supervisor-backed installs), grouped into records: a header line starts one and every following line that is not itself a header joins it โ traceback frames, the blank lines around chained exceptions, and non-HA banners such as s6-overlay restart lines. '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 |
| compact | No | logbook / system only: save context. Logbook strips attribute dicts; system keeps the start and end of each message string within 2000 characters (counted in 'truncated_messages'). search still matches the full message; text inside the cut is not shown. To read one message whole, pass False with a narrow search and limit=1. | |
| end_time | No | Logbook only: end of the window (ISO datetime). | |
| device_id | No | Logbook only: native Core device filter - events attributed to this device and the context chains they caused. State changes of the device's entities are not included; filter by entity_id for those. Requires the WebSocket path. | |
| entity_id | No | Logbook only: restrict to one entity, or a comma-separated list of entity IDs. | |
| context_id | No | Logbook only: only events sharing this context - 'what caused this'. Cannot be combined with entity_id or device_id; works on both transports when used alone. | |
| hours_back | No | Logbook only: how many hours back to read. | |
| structured | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral context beyond the annotations: it explains bounded/capped windows, window_start/window_end reporting, has_more and next_offset pagination, where tracebacks are dropped, how counts are scoped, and that limit/order are ignored in structured mode. This is exactly the kind of operational detail an agent needs to use the tool correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately front-loaded with the purpose and then organized by source/behavior concerns. It is dense and long, but the complexity of 15 parameters and seven sources justifies much of the length; a few cross-parameter points are repeated rather than stated once.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the annotations, output schema, and exhaustive parameter schema, the description supplies the remaining critical context: source selection, structured versus raw behavior, windowing, pagination, and traceback handling. An agent has enough information to choose a source and interpret the result without needing further external documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the input schema already documents each parameter in depth. The description synthesizes some cross-parameter behavior and introduces pagination-related concepts such as window_lines and next_offset, but most of its parameter-specific content repeats or summarizes what is already in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Get Home Assistant logs from various sources.' It clearly signals log retrieval across multiple backends, but it does not explicitly distinguish this tool from nearby alternatives such as ha_get_history or ha_get_automation_traces, so it falls short of a full 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear usage guidance among modes: prefer source='system' for triage, use structured=True for per-component rollup, and use structured=False or raw-text sources when broader raw coverage is needed. It stops short of naming sibling tools or describing when not to use this tool versus alternatives, so it is strong 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_get_operation_statusGet Operation StatusARead-onlyIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| operation_id | Yes | Single operation ID, or a list of IDs for a bulk status check. | |
| timeout_seconds | No | Seconds to wait for a pending operation to finish before returning its status. 0 returns the current status at once. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so safety is covered; the description earns credit for disclosing non-obvious behavior: bulk checks poll concurrently under one shared timeout window and report per-item failures inside detailed_results rather than aborting the batch. It stops short of describing timeout-expiry outcomes, but that is largely covered by the schema's timeout_seconds text and 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short paragraphs, front-loaded with the core action, then batch semantics, then the disambiguation pointer. Every sentence carries information and none restates the title or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 annotations carry the safety profile. For a two-parameter read tool, the description covers purpose, batch behavior, and the alternative tool, leaving nothing an agent needs to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 both parameters are self-documenting in the schema. The description adds genuine meaning beyond the per-parameter text by explaining that timeout_seconds is a single shared window across all IDs in a bulk check and that failures surface per item rather than failing the call.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get the status of one or more device operations') and adds the distinguishing mechanism ('real-time WebSocket verification'). It explicitly names the sibling it is not ('For current entity states, use ha_get_state instead'), so 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when-to-use tied to a producer ('track the operation_ids that ha_bulk_control returns') and an explicit alternative for the adjacent-but-different case ('For current entity states, use ha_get_state instead'). It also flags the batch mode as the intended path for multi-ID checks.
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 OverviewARead-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.
mcp_client reports the MCP protocol version, client info and
client capabilities this call arrived with, for diagnosing
client-specific behaviour. capabilities: null means the request
carried none to read, not that the client declared none.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max total entities across all domains (default: unlimited for minimal, 200 for standard/full). | |
| fields | No | 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, mcp_client. 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), ``ha_mcp_update`` (when an update check applies) and ``mcp_client`` are emitted regardless of ``fields=`` projection. | |
| offset | No | Number of entities to skip for pagination | |
| domains | No | Filter to specific domains (e.g. 'light,sensor' or ['light','sensor']). None = all domains. | |
| detail_level | No | '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_state | No | Include state field for entities (None = auto based on level). Full defaults to True. | |
| include_entity_id | No | Include entity_id field for entities (None = auto based on level). Full defaults to True. | |
| include_notifications | No | Include active persistent notifications. | |
| max_entities_per_domain | No | Override default entity cap per domain (minimal=10, standard/full=unlimited). 0 = no limit on entities or states. | |
| include_dismissed_repairs | No | Include user-dismissed/ignored repairs. Dismiss or restore one with ha_manage_updates(action='ignore_repair' / 'unignore_repair'). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the bar is lower, yet the description adds substantive behavior: the fields= projection shortcut skips unrelated state/service/registry reads, domain counts and states_summary stay complete regardless of pagination, and settings_url vs settings_url_hint appear conditionally by deployment mode. It even explains that capabilities: null means the request carried none, not that the client declared none โ exactly the kind of non-obvious caveat 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first three paragraphs are front-loaded and high value (purpose, usage, projection shortcut). The trailing three paragraphs about settings_url, ha_mcp_update, and mcp_client document response fields, which is largely redundant given an output schema exists, and they bloat a tool definition an agent must parse before calling.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 10 optional parameters, an output schema, and full annotation coverage, the agent has everything it needs: cost model, detail-level guidance, projection semantics, and the do-not-use alternatives. Nothing required 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3; the description earns above that by adding cross-parameter semantics the schema does not: 'minimal' is the recommended default, and domain counts/states_summary remain complete no matter how entity pagination is set. It does not restate limit/offset/domains, which the schema already documents well.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ("Get system overview") and enumerates what the overview contains: base_url, version, location, timezone, entity overview, notifications. The lead-in phrase "AI-friendly ... with intelligent categorization" is marketing filler, but the concrete enumeration rescues it. Sibling differentiation is handled in the usage paragraph rather than here.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says to use 'minimal' (default) for most queries, then names the exact non-use cases and the correct alternatives: ha_get_state for one entity, ha_get_entity for registry metadata, ha_search for domain/area filters. It also warns the unprojected overview is expensive on large installations, giving the agent a cost-based selection criterion.
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 GuideARead-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
| Name | Required | Description | Default |
|---|---|---|---|
| file | No | Path of the file to read, exactly as SKILL.md links it (e.g. 'references/triggers-and-conditions.md'). Omit to read SKILL.md. | SKILL.md |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, but the description adds critical context annotations cannot: the skill is currently unavailable, calls return an error, the cause (missing skills directory or unparseable SKILL.md), and a remediation ('ask the operator to verify the skills-vendor submodule'). That is materially valuable behavior 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Core behavior, usage, and replacement routing are front-loaded and efficiently ordered. The trailing keyword-stuffing block is intentional discoverability metadata but reads as noise and slightly dilutes an otherwise tight structure.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given an output schema exists, return values needn't be explained. The description covers what the tool is, its current failure state, when to use it, and which tools it replaces โ everything an agent needs to select and invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is a single optional parameter and the schema description coverage is 100%, so the schema already documents the 'file' path semantics fully. The description adds no parameter-level detail beyond the schema, which is the expected baseline-3 case.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get the bundled Home Assistant best-practices skill') and immediately distinguishes itself from the deprecated siblings it replaces. An agent can tell exactly what it retrieves and which prior tools it supersedes 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use triggers: 'Use BEFORE' creating/editing automations, scripts, scenes, helpers, dashboards, and before calling specific config tools by name. It also names the alternatives ('Replaces ... ha_list_resources, ha_read_resource ...') with the routing rule 'call this instead.'
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 StateARead-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
at the top level of the result (sibling of data/metadata), outside
the projected record(s), 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 which lights
| Name | Required | Description | Default |
|---|---|---|---|
| fields | No | 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. | |
| entity_id | Yes | Entity ID or list of entity IDs to retrieve state for (e.g., 'light.kitchen' or ['light.kitchen', 'sensor.temperature']) | |
| attribute_keys | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare read-only/idempotent/non-destructive; the description adds substantive behavior the annotations cannot: partial-success semantics in bulk mode ('success=True if at least one state was retrieved'), the need to check 'error_count', the 100-item cap with dedupe, and parallel fetching. It also discloses the warning-emission side effect when attribute_keys is used outside an attributes projection.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Core behavior is front-loaded, the projection paragraph is dense but earns its length, and the EXAMPLES block is genuinely useful. The trailing keyword-salad line ('get current state value single entity check status bulk multiple states which lights') is unstructured noise that adds no information and slightly dilutes an otherwise disciplined layout.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a three-parameter read tool with a full output schema, this covers everything an agent needs: input modes, limits, projection semantics, partial-failure signaling, and where warnings surface relative to the projected record. Nothing required 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.
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 goes well beyond it: it explains that fields projects per-entity record keys rather than the outer bulk wrapper, that outer keys are always preserved, and that attribute_keys is a no-op unless 'attributes' is in fields. That interaction rule is invisible in the schema and is exactly the kind of semantics an agent would otherwise get wrong.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ('Get current status, state, and attributes of one or more entities') and enumerates the entity domains covered. It never names a sibling such as ha_get_entity, ha_get_history, or ha_get_overview, so the agent gets no explicit differentiation from those adjacent read tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description tells you how to invoke it in both modes (single string vs. list, max 100, deduplicated, fetched in parallel) and gives worked examples. It stops short of when-to-use guidance: nothing says to prefer ha_get_history for past states or ha_get_entity for registry metadata, so selection between siblings 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_system_healthGet System Health (incl. ZHA/Z-Wave/integration diagnostics)ARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| include | No | 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). | |
| device_id | No | 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. | |
| config_entry_id | No | Config entry ID of the integration (find via ha_get_integration). Required when include contains 'diagnostics'. | |
| diagnostics_fields | No | 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'. | |
| diagnostics_data_path | No | 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'. | |
| diagnostics_data_limit | No | Pagination window for list-valued diagnostics_data_path results; data becomes {path, items, offset, limit, total, has_more}. Only applies with include='diagnostics'. | |
| diagnostics_data_offset | No | Pagination start index for list-valued diagnostics_data_path results. Only applies with include='diagnostics'. | |
| include_dismissed_repairs | No | 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'). | |
| diagnostics_truncate_at_bytes | No | 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'. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered. The description adds genuine behavioral context beyond that: information varies by installation type, diagnostics payloads can be large, pagination behavior, and the ha_mcp_update field that should trigger a user notification.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose, then scope variability, then examples. Efficient overall, though the examples partially duplicate schema-level documentation, adding some redundancy for an already well-documented tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 needn't be re-explained, and the description covers the important extras (variable info by install, update notice, pagination loop). It is essentially complete for an agent to call the tool correctly, with only minor sibling-routing guidance missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so per-dimension baseline is 3. The description's EXAMPLES illustrate parameter usage (include combos, config_entry_id, diagnostics_data_path/limit/offset), but they largely restate semantics already documented in the schema rather than adding new meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 enumerates scope (Zigbee/ZHA, Z-Wave JS, per-integration diagnostics). An agent can distinguish it from siblings like ha_get_overview or ha_get_logs 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides two concrete invocation examples showing section selection and the pagination workflow, and gives a disposition rule ('tell the user a newer ha-mcp release is available'). It lacks explicit when-not-to-use guidance relative to sibling tools like ha_get_overview or ha_get_logs, 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_get_todoGet TodoARead-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 as Home Assistant reports them โ uid, summary, status, and description, due and completed where set.
EXAMPLES:
List all todo lists: ha_get_todo()
Get incomplete items: ha_get_todo("todo.shopping_list", status="needs_action")
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Only items with this status, or with any of a list of statuses: 'needs_action' (open) or 'completed' (done). Only applies when entity_id is provided. | |
| entity_id | No | Todo list entity ID (e.g., 'todo.shopping_list'). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnly, idempotent, non-destructive), and the description adds the useful framing that state means "number of incomplete items or current status" in list mode versus per-item fields in item mode, plus the caveat that Home Assistant reports items with due/completed fields only "where set." That partial-population caveat is genuine behavioral context not present in the schema. A dedicated note on sorting or truncation of large lists is the only missing piece.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the two-mode split, then mode details, then compact examples. Nothing is redundant with the schema, and the EXAMPLES block is short and illustrative rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-required-parameter read tool with full schema coverage, annotations, and an output schema, the definition supplies everything needed to choose the right mode and call correctly. Return-value enumeration is arguably surplus given the output schema, but it costs nothing here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 structured form, and the description largely restates the same scoping rule (status applies only with entity_id). It adds marginal value by setting expectations about what each mode returns, but no new parameter syntax or format detail. Baseline 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.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ("Get todo lists or items") and immediately splits the tool's two operating modes by presence/absence of entity_id. An agent can tell this apart from the mutating siblings ha_set_todo_item and ha_remove_todo_item from the name and description alone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly states the conditional behavior: without entity_id it enumerates the todo domain, with entity_id it returns that list's items, and the status filter "Only applies when entity_id is provided." Worked examples reinforce the calling pattern. It does not explicitly route to alternatives for writing items, but the read-only 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_get_zoneGet ZoneARead-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="office")
Same zone by entity_id: ha_get_zone(zone_id="zone.office")
Zones outside the zone storage, the home zone (from the general
settings) and YAML zones, are listed with editable=false /
source="yaml"; editable means ha_set_zone and ha_remove_zone can
change the zone. Each zone carries its entity_id.
| Name | Required | Description | Default |
|---|---|---|---|
| zone_id | No | Zone ID or entity_id to get details for (from the ha_get_zone() list). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnly, idempotent, non-destructive), yet the description still adds real value: zones outside zone storage, the home zone, and YAML zones come back with editable=false / source="yaml", and each zone carries its entity_id. That editable-flag semantics is non-obvious behavior an agent needs before deciding whether to attempt a write.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the two modes before examples, and the editable/YAML paragraph is placed last where it belongs. The final paragraph is a bit dense and slightly awkwardly worded, but nothing is wasted at the sentence level.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-optional-param read tool with annotations and an output schema, this is nearly complete: both modes' returns are described conceptually and the editable flag is explained. Only a clearer statement of what detail fields a specific-zone response contains (beyond entity_id) is missing, and the output schema likely covers that.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 further by showing that zone_id accepts both a bare ID ('office') and an entity_id ('zone.office'), which is a real ambiguity the schema leaves open. The examples resolve it concretely.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 splits the behavior cleanly into two modes: list-all without zone_id, detailed config with zone_id. An agent can immediately tell this apart from sibling mutators like ha_set_zone and ha_remove_zone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The no-arg vs. with-arg distinction is explicit, concrete examples are given for both list and detail calls, and the editable/source note implicitly routes the agent to ha_set_zone/ha_remove_zone for changes. It lacks an explicit 'do not use this to modify zones' exclusion, but the context is clear.
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 AreasARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| fields | No | 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. | |
| area_fields | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered. The description goes beyond them with genuinely useful behavior: level=None sorting alongside level 0, the unassigned vs orphaned classification rules, and a consistency caveat about the single-snapshot registry path vs the legacy dual-WebSocket path that may transiently misclassify. This is well past the lowered bar.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core listing behavior, then usage, then edge-case semantics โ a sensible ordering. The final registry-consistency paragraph is dense, but every claim (None-level sort order, orphaned classification, snapshot consistency) carries real invocation-relevant meaning rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description needn't explain return values, and annotations plus a fully covered input schema handle the rest. The description covers the residual behavioral gaps (sort ordering, classification, consistency) so 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% โ both fields and area_fields are fully documented in the schema, including available keys and projection behavior. The description adds nothing about parameters, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (list) and resource (floors and areas), and describes the exact structure returned: floors sorted by level ascending with nested areas, plus unassigned_areas and orphaned_areas. An agent can distinguish this from the write-oriented siblings (ha_set_area_or_floor, 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit context and worked examples ('which rooms are on the ground floor', level-scoped operations) that tell the agent when this tool is the right choice. It stops short of naming alternative tools or stating when not to use it, 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_list_servicesList Available ServicesARead-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)
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max services to return per page | |
| query | No | Search in service names and descriptions. | |
| domain | No | Filter by domain (e.g., 'light', 'switch', 'climate'). | |
| fields | No | 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. | |
| offset | No | Number of services to skip for pagination | |
| detail_level | No | 'summary': name, description, domain, service, and target when the service has one. 'full': additionally include parameter field schemas. | summary |
| service_fields | No | 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). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so the safety profile is fully covered structurally. The description adds the discovery-vs-invocation relationship and the pagination/detail-control behavior, but these largely restate what the schema already documents, so it adds only modest extra context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences plus three front-loaded one-line examples; every line earns its place. Slightly verbose in the examples format but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, 100% schema coverage, and rich annotations, the description only needs to establish purpose and routing. It does that, though it could say more about pagination limits interacting with the total result set.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 examples go beyond the schema descriptions by demonstrating concrete argument shapes for domain, query, offset, and detail_level (e.g. detail_level="full"), which helps an agent pick values correctly.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List available Home Assistant services') and explicitly frames the tool's role in the workflow: discovering services/actions that can then be called via ha_call_service. This distinguishes it cleanly from the sibling ha_call_service, which performs the call.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The EXAMPLES block gives concrete when-to-use patterns (filter by domain, search, paginate) and the second sentence routes the agent to ha_call_service for the actual invocation. There is no explicit statement of when NOT to use it or an alternative discovery path, 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_manage_appManage App (add-on)ADestructive
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
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
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | Proxy mode only. Request body for POST/PUT/PATCH โ or, with websocket=True, the initial WebSocket message. Pass a JSON object or JSON string. | |
| boot | No | Config mode: Boot strategy โ 'auto' (start with HA) or 'manual'. | |
| path | No | Proxy mode: API path relative to the app (add-on) root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode. ESPHome 2026.10+: POST JSON-RPC to '/api/mcp' (initialize, notifications/initialized, tools/list, then tools/call); check response.error first, then response.result.isError. | |
| port | No | 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 and ESPHome, reject direct access unless their leave_front_door_open option is enabled and the app is restarted. | |
| slug | No | 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). | |
| debug | No | Proxy mode only. Include diagnostic info (request URL, headers sent, response headers). | |
| limit | No | Proxy mode only. HTTP: return at most this many items from a JSON array response. | |
| action | No | 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'). | |
| method | No | Proxy mode only. HTTP method: GET, POST, PUT, DELETE, PATCH. | GET |
| offset | No | Proxy mode only. HTTP: skip this many items in a JSON array response. | |
| network | No | 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. | |
| options | No | Config mode: App (add-on) configuration values (the 'Configuration' tab in the UI). | |
| watchdog | No | Config mode: Enable or disable Supervisor watchdog (auto-restart on crash). | |
| summarize | No | 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. | |
| websocket | No | 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. | |
| repository | No | 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. | |
| array_patch | No | 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. | |
| auto_update | No | Config mode: Enable or disable automatic updates for this app (add-on). | |
| message_limit | No | Proxy mode only. WebSocket: cap on messages collected from the wire, bounded by an internal safety ceiling. None = collect up to the ceiling. | |
| message_offset | No | Proxy mode only. WebSocket: drop this many messages from the start of the collected list before returning. | |
| wait_for_close | No | 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. | |
| request_headers | No | 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. | |
| python_transform | No | 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)]. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already flag this as destructive, non-readonly, non-idempotent, and open-world, but the description adds substantial operational context beyond them: HA OS/Supervised is required, ha-mcp cannot update its own running slug, unknown lifecycle/config/repository outcomes must be verified with `ha_get_app`, and restart/rebuild outcomes cannot be proven by that verification. It also discloses merge/replace semantics for `options` and `network`, and retry guidance for proxy/array-patch writes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and mode-routing guidance, then proceeds to operational caveats in a mostly logical order. It is long, but the complexity of a 23-parameter multi-mode tool justifies much of the length; the trailing keyword list is the main conciseness weakness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, 23 parameters, and high schema coverage, the description is unusually complete: it covers mode selection, prerequisites, mutation risks, retry behavior, Supervisor limitations, and even a Claude Desktop-specific timeout workaround. Because an output schema exists, it does not need to explain return values in detail.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 the individual parameters thoroughly. The description still adds useful cross-parameter semantics: exactly one mode must be selected, `options` merges top-level keys and one nested level, and deeper nested mappings are replaced rather than merged.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: managing Home Assistant apps/add-ons and proxying app APIs. It also distinguishes this tool from the sibling `ha_get_app` by directing inventory/status/metadata reads there first, so an agent can tell the tools apart without opening both schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit routing rules: use `ha_get_app` first for inventory/status/metadata, use proxy mode only for documented app-specific read APIs, and do not infer private schemas. It also specifies that exactly one mode must be used and explains the prerequisites and constraints for each mode.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_manage_backupManage BackupsADestructive
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 |
|
| Without any |
|
| List full HA tarball snapshots as Home Assistant reports them (id, name, date, per-location size). Read-only โ use to discover a |
|
| Restore from a full HA tarball. Replaces Home Assistant's configuration plus what |
|
| Delete one full HA tarball by |
|
| On-demand snapshot of one entity ( |
|
| List per-entity auto-backups (lightweight). Filter by |
|
| Read one auto-backup file by name; returns YAML and parsed |
|
| 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. |
|
| Re-apply one auto-backup. Existing flow helpers (template, group, utility_meter, โฆ), config subentries and integration config entries require a fresh per-entity backup; other domains follow auto-backup settings and may proceed without one. A deleted flow helper is recreated with a new config-entry ID and its saved entity IDs are restored if unoccupied; a deleted subentry is recreated with a new subentry ID. An integration config entry's snapshot holds its metadata, its enabled state and its options, which are kept only when the ha_mcp_tools component serves the read, its |
|
| Delete one auto-backup by |
When to use which scope:
scope="edits": ha-mcp's own per-entity YAML backups of automations, scripts, scenes, dashboards and helpers. Use them to undo a recent edit by the agent. Lightweight, fast, no restart.scope="snapshot": Home Assistant's own backups โ a full backup (createwithoutinclude_*), or a custom backup of some parts, such as one or more apps (include_addons). Restore one only for system-wide recovery (mass config corruption, a broken app update with that app inrestore_addons, etc.).
A snapshot backup is the user's call: before a change that cannot be undone, you may ask the user whether they want one first; make it only when they say so. A full backup is large, and an unrequested one can push an older backup out of retention.
(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.
Human-managed backup controls: enable_snapshot_actions=false (ENABLE_SNAPSHOT_ACTIONS) blocks every snapshot action, including listing. backup_read_only=true (BACKUP_READ_ONLY) allows edits list/view/diff and snapshot list when snapshots are enabled; it blocks explicit create/restore/delete in both scopes. Automatic pre-edit capture still follows enable_auto_backup. A human must change these controls in the Backups tab, app configuration, or environment; developer tools cannot change them. Global and connection Read Only Mode still restrict writes.
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.
Flow-helper filters: edits.create accepts a flow helper's 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:
Full backup, when the user asks for one:
ha_manage_backup(scope="snapshot", action="create")Backup of one app:
ha_manage_backup(scope="snapshot", action="create", include_addons=["core_mosquitto"])List snapshots (to discover a backup_id or confirm one landed):
ha_manage_backup(scope="snapshot", action="list")Restore a snapshot's configuration (the database follows the backup):
ha_manage_backup(scope="snapshot", action="restore", backup_id="dd7550ed")Restore a snapshot with one of its apps:
ha_manage_backup(scope="snapshot", action="restore", backup_id="dd7550ed", restore_addons=["core_mosquitto"])Restore after a backup of what it replaces, when the user chose one:
ha_manage_backup(scope="snapshot", action="restore", backup_id="dd7550ed", safety_backup=True)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="welcome_home_active")List recent auto-backups for one automation:
ha_manage_backup(scope="edits", action="list", domain="automation", entity_id="welcome_home")View an auto-backup:
ha_manage_backup(scope="edits", action="view", backup_name="automation.welcome_home.20260521_153000.yaml")Diff an auto-backup vs current state:
ha_manage_backup(scope="edits", action="diff", backup_name="automation.welcome_home.20260521_153000.yaml")Restore an auto-backup:
ha_manage_backup(scope="edits", action="restore", backup_name="automation.welcome_home.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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | (snapshot.create) Tarball name. Auto-generated if not provided. Home Assistant names a full backup made with its automatic-backup settings itself. | |
| limit | No | (edits.list / snapshot.list) Maximum number of entries to return. | |
| scope | Yes | 'snapshot' for full HA tarballs; 'edits' for per-entity auto-backups. | |
| action | Yes | Operation to perform. Valid (scope, action) combinations are listed in the tool description. | |
| domain | No | (edits.create / edits.list / edits.delete) Filter auto-backups by domain (e.g. 'automation', 'helper_timer'). Required for edits.create. | |
| confirm | No | (snapshot.delete) Must be True to confirm deletion. | |
| backup_id | No | (snapshot.restore / snapshot.delete) Tarball ID (e.g. 'dd7550ed'). | |
| entity_id | No | (edits.create / edits.list / edits.delete) Filter auto-backups by entity ID. Required for edits.create. | |
| backup_name | No | (edits.view / edits.restore / edits.delete) Auto-backup filename (format '<domain>.<entity_id>.<timestamp>[_NN].yaml'). Not a tarball ID. | |
| safety_backup | No | (snapshot.restore) Back up what the restore replaces (configuration, plus the database, apps and folders it restores) first, as a manual backup on the default location. Off by default; offer it to the user, who decides. Needs the default backup password. | |
| include_addons | No | (snapshot.create) Custom backup: slugs of the installed apps (add-ons) to include. Supervisor installs only. | |
| restore_addons | No | (snapshot.restore) Slugs of apps (add-ons) in the backup to restore, from its `addons` in the snapshot list. Default: none. Supervisor installs only. | |
| include_folders | No | (snapshot.create) Custom backup: folders to include (`share`, `addons/local`, `ssl`, `media`). Supervisor installs only. | |
| older_than_days | No | (edits.delete) Bulk-delete auto-backups older than this many days. | |
| restore_folders | No | (snapshot.restore) Folders in the backup to restore, from its `folders` in the snapshot list (`share`, `addons/local`, `ssl`, `media`). Default: none. Supervisor installs only. | |
| include_database | No | (snapshot.create) Custom backup: include the database (history); needs include_homeassistant=True. | |
| restore_database | No | (snapshot.restore) Include the database in the restore. Default: restore it when the backup contains it. Must be false for a backup without one; on Supervisor installs it must match the backup. | |
| include_all_addons | No | (snapshot.create) Custom backup: include every app (add-on); not together with include_addons. Supervisor installs only. | |
| include_homeassistant | No | (snapshot.create) Custom backup: include Home Assistant's configuration. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say destructiveHint=true and readOnlyHint=false; the description goes far beyond by disclosing that snapshot restore restarts HA while edits restore does not, that snapshot delete is disabled by default and guarded by age/newest-snapshot floors, that enable_snapshot_actions/backup_read_only/enable_auto_backup are human-only controls, that a default backup password is needed, and that progress heartbeats are sent during long creates. It also warns about retention eviction and a Claude Desktop timeout workaround.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The 'Pick the scope first' instruction is correctly front-loaded and the table/section/example structure is navigable, but the description is grossly oversized for a tool description โ the (snapshot, create) cell alone is a multi-clause run-on of ~200 words. Substantial content is repeated three times (table row, 'When to use which scope' section, examples), and the Claude Desktop timeout workaround is appended without a heading, which reads as unrelated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 19-parameter, multi-mode tool with an output schema and destructive annotations, the description covers the decision logic, per-action side effects, restart behavior, permission gates, and failure modes. Nothing an agent needs to invoke a given (scope, action) pair correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so a 3 would be the baseline, but the description adds genuine cross-parameter meaning: include_* presence flips snapshot.create from full to custom backup, include_all_addons cannot combine with include_addons, include_database requires include_homeassistant, restore_database must match the backup, and backup_name (filename format) is explicitly not a backup_id. It also explains that edits.create on a flow helper returns a config-entry ID to be used for later list/delete filters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb+resource (manage Home Assistant backups) and immediately splits it into two named scopes ('snapshot' for full HA tarballs, 'edits' for per-entity auto-backups), with a table mapping every (scope, action) pair to concrete behavior. An agent can distinguish this tool from siblings like ha_config_set_automation or ha_restart 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is an explicit 'When to use which scope' section that names the condition selecting each scope (undo a recent agent edit vs system-wide recovery), plus exclusions ('Restore one only for system-wide recovery') and a policy constraint ('A snapshot backup is the user's call'). Fourteen worked examples map user intents to exact call signatures.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_manage_blueprintsManage BlueprintsADestructive
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
blueprint blueprints import delete remove unused substitute take-control list ha_get_blueprint ha_import_blueprint
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | URL to import from โ GitHub, Home Assistant Community, or a direct YAML link (action='import') | |
| path | No | Installed blueprint path, e.g. 'homeassistant/motion_light.yaml' (action='get' / 'save' / 'delete' / 'substitute'). 'save' appends '.yaml' when it is missing, as Home Assistant does. | |
| yaml | No | Blueprint YAML text to write (action='save') | |
| input | No | Blueprint input values keyed by input name (action='substitute'); defaults to {} | |
| action | Yes | '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") | |
| domain | No | Blueprint domain: 'automation' or 'script'. Ignored by action='import' โ the blueprint file declares its own domain. | automation |
| confirm | No | Required confirmation for action='delete' | |
| overwrite | No | Write over an already-installed blueprint (action='import' / 'save'). Home Assistant reloads every automation/script using it. | |
| source_url | No | Origin URL to stamp into the saved blueprint's metadata (action='save'); omit for a hand-authored blueprint |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, openWorldHint=true, idempotentHint=false and readOnlyHint=false, so the risk profile is covered. The description nonetheless adds genuinely useful behavior the annotations cannot: get may return no YAML on a bare install, yaml_source identifies the reader, source_url is a fresh download, both writes are snapshotted for backup, substitute writes nothing, and HA refuses to delete an in-use blueprint. This is above the annotation baseline but stops short of the richness expected at 5 given how much it already front-loads.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the lifecycle summary and the key exclusion, then caveats and worked examples. Dense but largely earns its length. The trailing Claude Desktop timeout workaround is operational trivia that dilutes an otherwise well-structured description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a nine-parameter, six-action tool with an output schema and annotations present, the description covers lifecycle routing, caveats about unreadable YAML, backup interaction, and cross-tool handoffs. Nothing an agent needs in order to choose an action correctly appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 still adds cross-parameter semantics the schema cannot express: getโsave with a new path duplicates, same path with overwrite=True edits in place, and substitute's output must be handed to ha_config_set_automation/script to persist. These pairings of action+parameter are meaningful beyond the per-parameter schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Explicitly names the resource (Home Assistant blueprints) and enumerates the six operations (list, read, import, save, delete, render). It also carves the boundary against the most confusable siblings by stating this is not the tool that creates automations/scripts FROM a blueprint.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-not guidance ('DO NOT use this to create an automation or script FROM a blueprint' โ names ha_config_set_automation / ha_config_set_script) plus concrete workflows for duplication and in-place edits, and a pre-delete check via used_by. Alternatives (take_control_of_blueprint=True for existing items) are named with their selection condition.
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 PreferencesADestructive
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 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.
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.
mode='add_source': append a single entry to
energy_sources. Same optimistic read-modify-write semantics.
RELATED TOOLS: Use ha_get_history(source="statistics") with the returned statistic IDs for consumption, totals, and trends. Metadata comes directly from the running Core, including stored and display units.
CAVEATS:
energy/save_prefshas per-key FULL-REPLACE semantics. Passing{"device_consumption": [<one entry>]}deletes every other device the user had configured โ silently, with no error.The per-key
config_hashform lets an agent submit only the top-level key it wants to change:configkeys must equal the dict keys, and a per-key submission still fully replaces that key's value. A mismatch on any locked key returnsRESOURCE_LOCKEDwith the offending keys inmismatched_keys.dry_run=Trueskips 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/validateand returns residual issues (missing stats, unit mismatches) aspost_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.
EXAMPLES:
ha_manage_energy_prefs(mode="get", include_schema=True)
Preview a device: ha_manage_energy_prefs(mode="add_device", stat_consumption="sensor.fridge_energy", dry_run=True)
Remove a device: ha_manage_energy_prefs(mode="remove_device", stat_consumption="sensor.fridge_energy")
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
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
| Name | Required | Description | Default |
|---|---|---|---|
| mode | Yes | Operation mode. Primitives: 'get' reads the current prefs; 'set' writes a full prefs payload. | |
| name | No | Display name for mode='add_device'; ignored otherwise. | |
| water | No | If True, mode='add_device' / 'remove_device' targets 'device_consumption_water' instead of 'device_consumption'. | |
| config | No | 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. | |
| source | No | Single native energy_sources entry for mode='add_source'. Use mode='get', include_schema=True to inspect the running Core's fields and requirements; dry_run=True validates without saving. | |
| dry_run | No | If True, preview without saving. With the component, validates the complete proposal through Core's registered save schema. Without that capability, explicitly reports proposal validation unavailable. Semantic energy/validate checks persisted state only. | |
| config_hash | No | 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. | |
| include_schema | No | With mode='get', describe the running Core's save schema. Unavailable without the component. | |
| included_in_stat | No | '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_consumption | No | Statistic entity_id for mode='add_device' / 'remove_device' (e.g. 'sensor.fridge_energy'). Required for those modes; ignored otherwise. | |
| include_statistics | No | With mode='get', include native recorder metadata and resolved output units for all configured statistic references. Ignored for other modes. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true, idempotentHint=false, readOnlyHint=false, and the description substantiates that with concrete consequences: full-replace semantics silently delete other devices, RESOURCE_LOCKED mismatch reporting, admin-token requirement, dry_run skipping the hash check, and post_save_validation_errors being returned while the save still persists. This is exactly the behavioral context structured fields 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the WHEN NOT TO USE / WHEN TO USE / CAVEATS sections are easy to scan. However, the trailing keyword dump and the lengthy Claude Desktop timeout workaround paragraph dilute focus and blur whether the section is guidance or an operational note, adding bulk that isn't core to tool selection.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given an 11-parameter tool with an output schema, destructive annotations, and a complex hash-locked write path, the description covers safety, prerequisites, mode selection, failure modes, and validation behavior. Nothing material an agent needs before invoking it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is already 100%, so the baseline is 3, but the description adds meaning the schema lacks: add_device/remove_device perform an internal fresh read-modify-write, add_source uses optimistic append semantics with type-specific duplicate rejection, and config_hash dict-vs-str form selection is called out. The mode-to-behavior mapping isn't fully reproduced from the schema, so it earns above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names the specific resource (Home Assistant Energy Dashboard preferences) and enumerates the exact config domains it governs (grid/solar/battery/gas/water sources, device consumption sensors, cost tariffs). It also names a related sibling (ha_get_history) and its role, so an agent can distinguish it from neighboring tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is an explicit 'WHEN NOT TO USE' section (statistics must pre-exist as HA entities), a 'WHEN TO USE' section mapping every mode to its intended scenario, and a 'RELATED TOOLS' pointer to ha_get_history with returned statistic IDs. The conditions that select each mode are spelled out rather than inferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_manage_hacsManage HACSADestructive
Manage HACS (Home Assistant Community Store) โ install/update, remove, add custom repositories, or refresh repository information.
On Home Assistant 2026.11+ HACS is the built-in Marketplace; the tool
uses whichever the instance has. 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 ("Refresh information" in the Marketplace) โ a forced re-fetch of
one repository's release data from GitHub, so a pending update becomes
visible to the store 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")
Accept the Marketplace warning: ha_manage_hacs(action="accept_warning", confirm=True)
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 (the Marketplace only with a
connected GitHub account), so update_information is the way to surface a
just-published release. The Marketplace refuses installs until the user this server
connects as has accepted its first-run warning; Home Assistant keeps that per user,
so the user's own acceptance in the UI does not cover this server. Ask the user
whether they have read it (Settings > Marketplace) and accept it before
accept_warning. Adding a repository needs a GitHub account the user connects in
the Marketplace panel.
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | 'download' to install/update, 'add_repository' to register a custom repo, 'remove' to uninstall a downloaded repo, 'update_information' to refresh a repository's release data from GitHub, or 'accept_warning' to accept the Marketplace's first-run warning for the user this server connects as | |
| confirm | No | action='accept_warning': True once the user has said they read the Marketplace's first-run warning and accept it | |
| version | No | Specific version to install (action='download') | |
| category | No | Repository category (action='add_repository'). One of the categories the store reports (search's store.categories); 'lovelace' means 'plugin' (dashboard cards). | |
| repository | No | GitHub repo 'owner/repo' to add (action='add_repository') | |
| repository_id | No | Numeric HACS ID or 'owner/repo' path (action='download' / 'remove' / 'update_information') | |
| confirm_replace_built_in | No | Marketplace only (action='download'): install a repository that replaces the built-in integration of the same domain. Set it only after the user agrees. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and readOnlyHint=false, but the description adds substantial behavior beyond them: restart requirements after installing an integration, browser cache clears for Lovelace cards, the fact that removed integration modules persist until restart, the ~48h autonomous refresh interval, and the per-user nature of the Marketplace first-run warning. These are operational consequences an agent could not infer 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then examples, then caveats โ a sensible progression with no filler in the main body. The trailing Claude Desktop timeout workaround is long and only relevant to one client, which slightly dilutes focus, though it is clearly scoped as environment-specific.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a seven-parameter destructive tool with an output schema already covering return values, the description covers prerequisites, side effects, sequencing (delete config entries before removing an integration), and authorization requirements (per-user warning acceptance, GitHub account connection). Nothing an agent needs to call it safely is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 formats for action, repository_id ('owner/repo' or numeric ID), and category. The description reinforces a few constraints (owner/repo format, matching category, confirm gating) but adds little meaning that isn't already in the parameter descriptions, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific resource (HACS / Home Assistant Community Store) and enumerates the exact operations: install/update, remove, add repository, refresh info. It explicitly distinguishes itself from the read-only sibling ha_get_hacs_info, so 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when to use this tool versus the alternative ('to search the store or read repository details use ha_get_hacs_info'), and gives a per-action example block covering all five enum values, including the non-obvious update_information semantics. Explicit guidance on when add_repository vs download applies ('requires owner/repo format plus a matching category') is also present.
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 PipelineADestructive
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Pipeline display name. Required when action='create'. | |
| action | Yes | Pipeline operation: list, get, create, update, set_preferred, or process. | |
| agent_id | No | 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. | |
| language | No | Pipeline language, e.g. 'en'. For process, the language to recognise the sentence in. | |
| sentence | No | Natural-language command to run through Assist. Required when action='process'. | |
| tts_voice | No | Text-to-speech voice. Pass empty string to clear. | |
| stt_engine | No | Speech-to-text engine. Pass empty string to clear. | |
| tts_engine | No | Text-to-speech engine. Pass empty string to clear. | |
| pipeline_id | No | 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. | |
| stt_language | No | Speech-to-text language. Pass empty string to clear. | |
| tts_language | No | Text-to-speech language. Pass empty string to clear. | |
| wake_word_id | No | Wake-word ID. Pass empty string to clear. | |
| make_preferred | No | For create/update only, also set the resulting pipeline as preferred. Ignored for other actions. | |
| conversation_id | No | For process only, the conversation to continue. Returned in the response so follow-up sentences keep their context. | |
| base_pipeline_id | No | Pipeline ID to clone when creating. Omit to clone the preferred pipeline. Ignored for non-create actions. | |
| wake_word_entity | No | Wake-word entity ID. Pass empty string to clear. | |
| conversation_engine | No | Conversation agent entity ID or engine ID. Required when action='create'. | |
| prefer_local_intents | No | Whether Home Assistant local intents should be preferred before the conversation engine. | |
| conversation_language | No | Conversation language, usually '*'. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false, destructiveHint=true, openWorldHint=true, idempotentHint=false. The description goes well beyond that: action='process' actually executes the matched intent (turning on a light rather than reporting), errors arrive as response_type='error' with error_code such as 'no_intent_match' rather than a raised exception, and conversation_id must be threaded to preserve context. It even discloses a Claude Desktop-specific timeout/hang workaround.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The text is front-loaded with the action taxonomy, then behavior, then examples, then edge-case caveats โ a sensible progression, and nearly every sentence carries information. It is verbose for a description though, and the paragraph on sentence triggers versus intents ('that agent checks its sentence triggers before it matches intents, so this is not limited to intents') restates the same point in adjacent clauses, costing readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 19 parameters, an output schema, and full annotation coverage, this is a complex tool, and the description covers everything an agent needs: per-action requirements, the 'preferred' alias, empty-string clearing, error-shape expectations, and conversation continuation. Nothing required to invoke it correctly is left to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and each parameter is already documented there, so the baseline is 3. The description still adds meaning the schema does not: the 'preferred' sentinel for pipeline_id, empty-string clearing conventions shown in examples (tts_voice=""), the rule that non-nullable fields such as name/language/conversation_engine must be omitted or non-empty, and base_pipeline_id cloning behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource ('Manage Home Assistant Assist pipelines') and then enumerates all six actions with their distinct semantics (list/get/create/update/set_preferred/process). It explicitly contrasts itself with the sibling ha_call_service, 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Every action has a stated use, and the boundary against the nearest alternative is explicit: 'Use ha_call_service to act on an entity directly; use this to test what Assist itself understands.' The create/update/process examples show concrete invocation contexts, and the note on preferring a full pipeline run for non-built-in agents clarifies when this tool alone is insufficient.
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)ADestructive
Manage Home Assistant radios โ Z-Wave, Zigbee, Matter, and Thread.
For node diagnostics and network summaries prefer ha_get_device / ha_get_system_health, which mirror 'diagnostics' and 'network_status'. This tool also exposes active 'ping' probes and parameter reads. Z-Wave 'get_config_params' lists cached configuration values/metadata; 'get_config_param' reads one from the cache or explicitly from the device. Neither enables or creates entities. 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. HA administrator access is required for parameter reads.
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| radio | Yes | Which radio to manage. | |
| action | Yes | Operation 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'. | |
| params | No | Action-specific parameters (e.g. code, pin, channel, property, value). Z-Wave get_config_param: property (parameter number), endpoint (default 0), property_key (partial-parameter bit mask), refresh (default False; full root parameters only). | |
| confirm | No | Required (True) to run destructive actions (e.g. remove_device, network restore, change_channel, hard_reset, remove_fabric). | |
| device_id | No | Target device (node) for node-scoped actions. | |
| entity_id | No | Resolve the device from this entity for node-scoped actions. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: long-running actions return immediately with long_running=true and complete out-of-band, S2 interactive pairing is not scriptable, admin access is required for parameter reads, and destructive actions are enumerated. It also clarifies that config-param reads neither create nor enable entities, resolving the mixed read/write nature of a tool whose annotations mark it destructive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then alternatives, then caveats โ a sensible ordering. The final Claude Desktop timeout/manual-approve paragraph is operationally useful but verbose and client-specific, diluting an otherwise tight description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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; the description instead supplies the behavioral context an agent needs (long-running semantics, auth, action-discovery fallback, destructive-action confirmation). Complete for a broad multi-action tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 distinguishes 'get_config_params' (cached list) from 'get_config_param' (cache or explicitly from device), explaining the refresh/scope behavior behind the params. It does not enumerate every action-specific param, but the schema plus the unknown-action-returns-list convention covers that.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Manage Home Assistant radios') and enumerates the four radio types it covers. It explicitly routes diagnostics to sibling tools ha_get_device / ha_get_system_health, so an agent can distinguish this tool from related readers 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use-this vs alternatives ('For node diagnostics and network summaries prefer ha_get_device / ha_get_system_health'), and calls out the non-scriptable case (interactive S2 PIN inclusion) with a redirect to SmartStart/QR or the HA UI. Prerequisite (HA administrator access) is stated.
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 ThemesADestructiveIdempotent
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 (the Marketplace on 2026.11+, both via 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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Which mode the theme applies to when action='set'. Defaults to light. | |
| force | No | action='set_engine_theme' only: skip the expected_current guard and overwrite unconditionally. | |
| value | No | Frontend user-data theme object when action='set_engine_theme', e.g. {'theme': '', 'dark': False}. An empty dict restores default/auto behavior. | |
| action | Yes | 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'. | |
| theme_name | No | Theme 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_current | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true and idempotentHint=true, and the description adds real context beyond them: action='set' only changes the backend default so users with explicit profile choices are unaffected, theme names are validated server-side at call time, and the screenshot engine's token-owned user theme gets flipped as a side effect of screenshots. It also discloses the expected_current guard's non-atomic, best-effort nature and an operational timeout/hang workaround, none of which is 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The text is front-loaded with purpose, then when-not/when-to, then the specialized screenshot-engine layer, caveats, and examples, so an agent can stop reading early once it has what it needs. It is on the long side and repeats the expected_current guard behavior already spelled out in the schema, and the trailing Claude Desktop timeout paragraph is operationally useful but stylistically tacked on.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter, four-action tool with destructive side effects, the description covers the alternative tools, the two distinct theme layers (backend default vs per-user engine profile), the guard semantics, and usage examples. An output schema exists, so return values need not be described, and 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and each parameter already has a detailed description, so the baseline is 3. The description adds cross-parameter workflow semantics the schema cannot convey on its own โ e.g. passing BOTH the value and expected_current from a screenshot warning so a subsequently changed theme is not clobbered โ plus worked examples for each action/mode combination.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Manage Home Assistant frontend themes') and then enumerates the four concrete operations (list, set, get_engine_theme, set_engine_theme), which lets an agent distinguish this tool from siblings without opening the schema. It also explicitly disambiguates from ha_manage_hacs and ha_config_set_yaml for the theme-file workflows this tool cannot do.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is an explicit 'When NOT to use' block naming the correct alternative tools (ha_manage_hacs for installing, ha_config_set_yaml for editing), and a 'When to use' block mapping each action to its purpose. The screenshot-engine section further explains when to reach for get_engine_theme/set_engine_theme versus the backend default action='set'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_manage_updatesManage UpdatesADestructive
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.
'install' blocks until each update finishes (minutes). One that outlasts the request timeout ends the call with a connection timeout while it keeps running, and a Core update restarts Home Assistant; neither means failure, but later batch targets were not started. Confirm per entity with action='get'; finished updates drop out of 'list', which 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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
update updates install skip firmware core os repair repairs issue ignore dismiss unignore
| Name | Required | Description | Default |
|---|---|---|---|
| action | No | '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 |
| backup | No | For install: create a backup before installing where the update entity supports it (apps (add-ons)). | |
| repairs | No | For ignore_repair / unignore_repair: the Repairs issues to act on, as [{'domain': ..., 'issue_id': ...}] taken from ha_get_overview's repairs. | |
| categories | No | 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. | |
| entity_ids | No | Update entity_id(s) to act on. 'get' takes exactly one; skip/clear_skipped require at least one; for install, mutually exclusive with categories. | |
| include_skipped | No | For list: include updates that have been skipped. | |
| include_release_notes | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructive and non-read-only behavior, but the description adds substantial beyond-schema context: install blocks for minutes, timeout can occur while the update continues, Core updates restart Home Assistant, batch targets after a timeout may not have started, and finished updates disappear from list. It also notes the Claude Desktop timeout workaround and the ha_mcp_update status returned by list.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded and well organized for a complex multi-action tool, moving from purpose to scope to special cases and examples. It is on the long side and includes a keyword tail plus a very specific Claude Desktop workaround, but the length is largely justified by the tool's complexity and destructive operations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the rich input schema, output schema, annotations, and the tool's complexity, the description covers the key operational caveats an agent needs: read-only mode behavior, repair listing source, install blocking/timeout semantics, post-install verification, and the extra ha_mcp_update status. No critical call-selection context appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 the parameters in detail. The description reinforces some semanticsโrepairs come from ha_get_overview, categories exclude core/os/supervisor, entity_ids constraints for get/skip/installโbut much of this repeats the schema rather than adding format or behavior the schema lacks.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource and enumerates the actions: list/get/install/skip for updates and ignore/unignore for Repairs. It also distinguishes the scope from siblings by explicitly saying Repairs are listed by ha_get_overview, not here, and mentions ha_mcp_update returned by list.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear routing guidance: Repairs are listed by ha_get_overview and passed here as domain/issue_id, read actions remain available in Read Only Mode while writes are blocked, and install categories exclude core/os/supervisor so those must be targeted individually. The examples reinforce the main usage patterns without leaving alternatives implicit.
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 ComponentsADestructive
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| target | No | 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'. | all |
| entry_id | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true and idempotentHint=false, and the description adds real context beyond them: the entry_id single-component fast path, the validation error when combining target and entry_id, and a Claude Desktop timeout/hang workaround. It does not explain *why* the call is destructive (e.g. what running automations are interrupted), which is the main 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and trigger conditions are front-loaded and tight, but the closing Claude Desktop paragraph is long, hedged and tangential to the tool's function ('read the target back before repeating it... clicking it too quickly is known to cause hangs'), diluting an otherwise efficient definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers what to reload, when to use it, two invocation modes, and a known client-side failure mode; an output schema exists so return values need no explanation. What's missing is the destructive consequence of reloads and any permission/auth prerequisites, given destructiveHint=true.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 invocation examples (target="automations", entry_id="abc123") that demonstrate the two mutually exclusive usage modes. It repeats the schema's mutual-exclusion note rather than adding new constraint detail, keeping it out of 5 territory.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (reload) and resource (Home Assistant configuration components) and immediately distinguishes itself from ha_restart by noting it avoids a full restart. An agent can tell it apart from the many sibling config setters/getters 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete trigger conditions: after editing automation/script YAML, adding YAML helpers, or changing customize.yaml or themes, and contrasts it with a full restart as 'much faster'. It lacks an explicit exclusion rule (e.g. when ha_restart is actually required), so it falls just 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_remove_area_or_floorRemove Area or FloorADestructiveIdempotent
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Area ID or floor ID to delete (use ha_list_floors_areas to find IDs) | |
| kind | Yes | Which registry to delete from: 'area' or 'floor' |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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 non-obvious behavior: what is unassigned rather than deleted, the risk to automations, and a client-specific timeout/hang workaround for Claude Desktop. This is real operational context the structured fields 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core removal semantics and cascading effects are front-loaded in short, high-value sentences. The final Claude Desktop workaround paragraph is longer and narrower in applicability, slightly diluting an otherwise tight definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With destructive annotations, a full schema, and an output schema present, the description only needs to supply side effects and caveats โ which it does thoroughly, including what is and isn't deleted and downstream automation impact.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both parameters are well documented in the schema (including the pointer to ha_list_floors_areas for IDs). The description adds nothing parameter-specific, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 kind enum makes the dual scope explicit. An agent can distinguish it from ha_set_area_or_floor and ha_list_floors_areas 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the consequences of use (entities/devices unassigned but not deleted, automations may break), which tells the agent when this operation is appropriate and when to warn the user. It does not explicitly name alternatives or preconditions (e.g., 'use ha_list_floors_areas first'), so it stops 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_remove_deviceRemove DeviceADestructiveIdempotent
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| device_id | Yes | Device ID to remove from the registry |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and idempotentHint=true, but the description adds context the annotations cannot: associated entities may be removed as a side effect, active devices are typically re-added by their integration, and integration support is a prerequisite. It also documents a known call-hang scenario, which is behavioral detail well beyond the 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The warning, usage rules, example, and alternative are front-loaded and each earns its place. The closing Claude Desktop timeout paragraph is disproportionately long and environment-specific, adding bulk that is irrelevant to most callers, which keeps this below a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 side effects, prerequisites, alternatives, and a known failure mode โ everything an agent needs to decide whether and how to invoke a destructive registry operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single parameter already carries a description ('Device ID to remove from the registry'), so the schema does the heavy lifting. The description adds only a sample invocation with a placeholder id, which confirms format but adds little semantic meaning. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Remove an orphaned device from the Home Assistant device registry') and scopes it to orphaned devices rather than any device. It also names the sibling alternative ha_set_device, so an agent can distinguish delete-from-registry from disable without inspecting either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('only for orphaned devices that are no longer connected'), when-not-to-use, and a concrete alternative with the exact call: ha_set_device(device_id=..., disabled_by="user"). It also states a hard precondition ('Works only when the integration supports removing devices'), which is exactly the kind of routing guidance that prevents misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_remove_entityRemove EntityADestructiveIdempotent
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, integration or stored helper is still there, the entity may be re-added on the next HA restart or reload; for a stored helper's entity a single-id removal names the tool that removes the helper. Added again, it gets back its registry settings while its config entry exists, and for 30 days after that entry, or an entity without one, was removed. 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). A lost connection stops the run; not_attempted lists the ids never sent (the failing one too if it never left), safe to retry. Only single-id removal is auto-backed-up.
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| entity_id | Yes | Entity ID, or a list of entity IDs, to remove from the entity registry (e.g., 'sensor.old_temperature'). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructive/idempotent/non-readonly, but the description goes well beyond them: permanence, re-add behavior on restart/reload, the 30-day registry-settings restore window, a 100-id bulk cap, sequential execution, and partial-failure semantics (skipped = not-found is idempotent, not_attempted is safe to retry). It also discloses that only single-id removal is auto-backed-up, which is critical pre-call information an agent cannot get 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded and well-sectioned with BULK MODE and EXAMPLES headers, but the restore-window sentence ("Added again, it gets back its registry settings while its config entry exists, and for 30 days after that entry, or an entity without one, was removed") is syntactically tangled, and the Claude Desktop paragraph is long relative to its narrow applicability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive single-parameter tool with annotations and an output schema, almost everything an agent needs is present: safety profile, alternatives, bulk limits, failure handling, and recovery semantics. The one gap is that the re-add / 30-day restore behavior is stated in a way that is hard to parse, which matters for a destructive operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents the string-or-array union, so the baseline is 3. The description adds real meaning beyond it: the array form caps at 100 ids, executes sequentially, and changes the return contract, which affects how an agent should batch calls.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
"Remove one or more entities from the Home Assistant entity registry" states a specific verb, resource, and cardinality. It is clearly separable from siblings like ha_remove_device (device registry), ha_remove_helpers_integrations (helper/integration level), and ha_set_entity (which it explicitly points to for disabling).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use ("Use only for orphaned or stale entity entries"), explicit alternative ("consider disabling instead: ha_set_entity(entity_id=..., enabled=False)"), a pointer to the helper-removal tool for stored helpers, and a verification step via ha_search / ha_get_entity. The only weakness is the garbled sentence about stored helpers, but the routing intent is still recoverable.
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 IntegrationADestructiveIdempotent
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; helper_type picks the path, or the entity registry does when an entity_id comes without one.
WHEN NOT TO USE:
Removing only an entity (without deleting its underlying helper or config entry) โ use
ha_remove_entityinstead.YAML-configured helpers โ they have no storage backend. Edit the YAML file and reload the relevant integration.
ha-mcp's own ha_mcp_tools entries are refused: manage them in the HA UI, or (server settings, developer mode on) ha_dev_manage_settings / ha_dev_manage_server.
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 (its storage id) or entity_id โ websocket delete.
FLOW helper_type (any helper flow Home Assistant lists: template, group, utility_meter, derivative, ...; custom helper integrations too)
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. An entity registered by another integration is refused with VALIDATION_INVALID_PARAMETER and nothing is deleted.
helper_type=None + entity_id โ the entity's registry entry names its helper (storage type or helper flow); an entity of any other integration is refused with VALIDATION_INVALID_PARAMETER.
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 cannot be removed raises a structured error rather than returning silent success: ENTITY_NOT_FOUND when the entity_id is absent from both the state machine and the entity registry, or for a bare helper_id no stored helper has (a bare helper_id on a FLOW target also raises it โ FLOW resolution needs a full entity_id); RESOURCE_NOT_FOUND when it exists but is not storage- or registry-managed (a YAML-configured helper, or an entity with a state but no registry entry), for a config entry the backend reports as 404, or for 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 whatever helper owns an entity: ha_remove_helpers_integrations(target="sensor.energy_peak", 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. ha_manage_backup(scope="edits", action="restore") recreates removed FLOW helpers whose options could be snapshotted, and config subentries. It cannot recreate custom helper integrations (their options are withheld) or otp (no stored options); re-add those yourself, otp through the user in 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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Wait for entity removal. Default: True. Ignored for a direct config entry delete (an entry_id target) or helper_type='config_subentry' (no entity poll, require_restart returned). | |
| target | Yes | What to remove. One of: (a) bare helper_id for SIMPLE helpers: the helper's storage id, not the name of its entity, 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, with helper_type omitted, e.g. value from ha_get_integration(); (d) parent config entry_id for a config subentry. | |
| confirm | No | Must be True to confirm removal. | |
| helper_type | No | Helper type of target; ROUTING says which value takes which removal path. | |
| subentry_id | No | Config subentry ID to remove when helper_type='config_subentry'. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=true, and the description adds substantial context: structured error codes, refusal cases, side effects on referenced automations, backup/restore limitations, and retry guidance. Nothing 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well structured with WHEN NOT TO USE, ROUTING, EXAMPLES, and WARNING headings, and it is front-loaded with the core action. The Claude Desktop timeout workaround is useful but makes the definition unusually long for a tool description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the destructive operation, rich annotations, complete schema, and output schema, the description covers routing, error behavior, side effects, and recovery limits thoroughly. An agent has enough information to choose and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 ROUTING section adds valuable combination semantics for target plus helper_type, including entity_id, entry_id, and subentry_id cases. It does not add much about the wait parameter beyond the schema, so it falls just short of 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: remove a Home Assistant helper or integration config entry. It clearly distinguishes this from the sibling ha_remove_entity and explains the multiple removal paths it unifies.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Has an explicit WHEN NOT TO USE section naming alternatives such as ha_remove_entity, YAML editing, and ha_dev_manage_settings. The ROUTING section maps parameter combinations to specific backend mechanisms, leaving little to inference.
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 ItemADestructive
Remove items from a Home Assistant todo list.
Give item or remove_completed=True, not both. Removal is permanent;
to mark an item done instead, use ha_set_todo_item() with
status="completed". removed lists the items the call asked for that
are gone afterwards; with remove_completed=True that includes a
completed item another client removed meanwhile. With auto-backup on,
each item is backed up before it is removed; warnings names a backup
not taken because the item could not be read or the backup could not
start, and with remove_completed=True an item gone afterwards that was
not completed when the list was read, so was not backed up.
EXAMPLES:
ha_remove_todo_item("todo.shopping_list", "Buy milk")
ha_remove_todo_item("todo.shopping_list", ["Buy milk", "Bread"])
ha_remove_todo_item("todo.shopping_list", remove_completed=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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| item | No | Item to remove, or a list of items - each the item UID (from ha_get_todo) or the exact item summary/name. A string that names no item but holds a JSON array of strings is that list. | |
| entity_id | Yes | Todo list entity ID (e.g., 'todo.shopping_list') | |
| remove_completed | No | Remove every completed item |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (destructiveHint=true only signals risk) by stating removal is permanent, describing the auto-backup-before-removal behavior, and defining what `warnings` means. However, the sentence explaining `warnings` is tangled and hard to parse on first read, and the Claude Desktop timeout paragraph is a tooling workaround rather than a disclosure of this tool's behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core rule and the alternative are front-loaded, which is good, but the backup/`warnings` sentence is a dense multi-clause run-on, and the trailing Claude Desktop paragraph is long relative to its value. The examples are well-chosen and earn their space.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers a destructive three-parameter tool thoroughly: permanence, backup, mutual exclusion, alternative, and even expected output fields, so an output schema is not strictly needed for return semantics. Only failure modes (entity not found, item name matching nothing) are left implicit.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 a genuine cross-parameter constraint absent from the schema: `item` and `remove_completed` are mutually exclusive. It also clarifies that strings may be a UID or an exact name, which the schema's anyOf does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ("Remove items from a Home Assistant todo list") and immediately contrasts itself with the sibling ha_set_todo_item() for the mark-done case. An agent can distinguish removal from completion 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
States the selection constraint explicitly ("Give `item` or `remove_completed=True`, not both") and names the alternative tool with the exact argument needed (ha_set_todo_item with status="completed"). 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_remove_zoneRemove ZoneADestructiveIdempotent
Remove a Home Assistant zone.
EXAMPLES:
Remove zone: ha_remove_zone("office")
Remove by entity_id: ha_remove_zone("zone.office")
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: Zones defined in YAML, and the home zone, cannot be removed here.
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Wait for the zone to be gone before returning. Set to False for bulk operations. | |
| zone_id | Yes | Zone ID or entity_id of the zone to remove |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the destructiveHint/idempotentHint annotations by naming the collateral risk (automations referencing the zone may fail) and the hard restrictions (YAML zones and home zone are not removable). The Claude Desktop timeout note adds an operational caveat that annotations cannot carry, though it is idiosyncratic and does not describe the returned result โ which the output schema covers anyway.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded, and the EXAMPLES/WARNING/NOTE blocks are terse and scannable. The closing Claude Desktop paragraph is the weakest element โ three sentences of client-specific debugging detail that dilute the otherwise tight structure.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, idempotent single-entity removal with full schema coverage, annotations, and an output schema, nothing an agent needs is missing: identifier resolution, failure modes, and prohibited targets are all present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 adds value by showing concrete accepted formats ("office" vs "zone.office"), clarifying that zone_id is polymorphic. The `wait` flag is left entirely to the schema, which documents it adequately.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Remove a Home Assistant zone") that is immediately distinguishable from siblings ha_get_zone and ha_set_zone. The examples show the two accepted identifier forms, so the agent knows exactly what this tool consumes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to ha_get_zone() to resolve a zone_id, and states two concrete exclusion cases where the tool must not be used (YAML-defined zones and the home zone). Both positive usage and when-not-to-use are covered.
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 FeedbackARead-onlyIdempotent
Get diagnostics and a finished GitHub issue for a bug, agent feedback or a feature request.
Use it before filing any GitHub issue about ha-mcp: a bug report
without this report is closed after 24 hours. report_type picks
the kind of report; if it is unclear which, ask: "Are you reporting a
bug in ha-mcp, giving feedback on how I used the tools, or requesting
a feature?"
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 a tool is missing or rejects as unknown
a parameter your tool list shows: a stale list (not a bug) is the usual cause.
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | One-line summary of what broke or what is requested, for the issue title. | |
| fields | No | 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. | |
| ai_model | No | Your own model identity, as specific as you know it. Do not invent a version. | |
| client_app | No | 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. | |
| tool_calls | No | The tool call(s) that produced the problem, verbatim: name, arguments and the (shortened) response. | |
| description | No | What went wrong in markdown: steps to reproduce, expected and actual behavior. For agent_behavior: what you did and what you should have done. For feature_request: what ha-mcp should do, why the user needs it, and what you tried. | |
| report_type | No | 'runtime_bug' when ha-mcp errored or behaved wrongly; 'agent_behavior' when the user says you used the wrong tool or worked inefficiently; 'feature_request' when the user wants ha-mcp to do something it cannot do yet. | runtime_bug |
| user_prompt | No | The user message that led to the problem, verbatim. | |
| user_comment | No | The user's own words on what went wrong or what bothered them, verbatim. Ask the user for it; never write it yourself. | |
| tool_call_count | No | Number of ha_* tool calls made since the issue started; determines how many log entries to include. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, and the description is consistent with that: it produces an issue_url link rather than filing on the server. Beyond the annotations it discloses that the response is LARGE and field-narrowable, that logs are embedded in issue_body but stripped from the URL to fit GitHub's limit, that secrets are redacted while error messages are kept, and that a textless call still returns diagnostics with placeholders.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical constraint (use before filing, else closed in 24 hours) is front-loaded and the paragraphs are organized by decision order: what it does, when to use, what to pass, how to read the response. It runs long with some redundancy in the quoted clarifying question and the instruction pointers, but nearly every sentence carries operational value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter, zero-required tool with an output schema, the description covers the gaps that matter: report-type disambiguation, the sequencing of instructions/missing_tool_hint checks, the anonymisation obligation, and the duplicate check. Since an output schema exists, it correctly avoids enumerating return fields in prose beyond the ones that change agent behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the schema carries most parameter meaning. The description still adds cross-parameter behavior the schema cannot express: `fields=` is the escape hatch for the oversized response, `report_type` drives which report text goes where, and omitting report text degrades gracefully to placeholders rather than failing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names a specific verb and two concrete outputs: diagnostics collection plus a ready-to-file GitHub issue (title, body, URL). It also scopes the resource to ha-mcp itself, so an agent can immediately tell this apart from the many ha_* configuration siblings that operate on Home Assistant rather than on the MCP server.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the trigger explicitly ('Use it before filing any GitHub issue about ha-mcp') and the consequence of skipping it ('a bug report without this report is closed after 24 hours'). It also handles ambiguity in report_type by giving the literal clarifying question to ask the user, and it names the alternative path (checking missing_tool_hint first) when a tool appears missing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_restartRestart Home AssistantADestructive
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. When this server runs inside Home Assistant (embedded), it replies before the restart starts and is unreachable until Home Assistant is back; with safe_mode it stays down until Home Assistant is next restarted normally, because it is itself a custom integration.
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Must be True to confirm the restart. | |
| safe_mode | No | Restart in safe mode: no custom integrations or custom frontend resources load until the next normal restart. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and non-idempotency, but the description adds substantial context beyond them: 1-5 minute downtime, automatic config validation, embedded-mode reply-before-restart behavior, and safe_mode persistence until a normal restart. The Claude Desktop timeout workaround is unusual but genuinely operational.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The WARNING and alternative routing are front-loaded, which is ideal. The Claude Desktop timeout paragraph is long and mostly tangential to the call itself, but it is concrete operational guidance rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 needn't be explained. For a destructive, non-idempotent restart action, the description covers downtime, validation, alternatives, embedded-mode edge cases, and failure handling โ everything an agent needs to call it safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline would be 3, but the description adds meaning: the EXAMPLE ha_restart(confirm=True) shows the required confirm pattern, and it clarifies safe_mode's persistence effect beyond the schema's terse wording. Slight redundancy with schema for confirm.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Execute a Home Assistant restart') and immediately differentiates against siblings: ha_reload_core for config changes and ha_get_system_health for pre-checks. An agent can distinguish this from any other tool in the list 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names alternatives and the conditions that select them ('For configuration changes, consider ha_reload_core()', 'to pre-check, call ha_get_system_health(include="config_check")'). It also warns about embedded safe_mode consequences, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_searchSearchBRead-onlyIdempotent
Search for entities (lights, sensors, switches, climate, etc.) by name, domain, or area โ AND inside automation/script/scene/helper/dashboard configurations โ in one call.
find entities configs lookup discover search lights sensors switches covers climate fans media_player binary_sensor device_tracker person weather automation script helper input_boolean input_number automations scripts scenes helpers dashboards
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results per surface (entities, configs). | |
| query | No | 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. | |
| fields | No | Project the response to the named top-level keys (e.g. ["entities", "automations"]); None = full response. Diagnostic / pagination keys are always retained so projection cannot hide partial / error state. Distinct from `result_fields` (which projects each entity record's keys). Available keys: success, query, entities, automations, scripts, scenes, helpers, dashboards, search_types, search_type, entity_total_matches, config_total_matches, count, offset, limit, has_more, next_offset, entity_has_more, entity_next_offset, config_has_more, config_next_offset, by_domain, state_filter_note, area_names, domain_filter, area_filter, message, warnings, errors, partial, partial_reason. | |
| offset | No | Number of results to skip for pagination. | |
| area_filter | No | 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. | |
| exact_match | No | Exact substring matching. Set False for fuzzy matching when the query may have typos. | |
| search_types | No | 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. | |
| state_filter | No | Filter entity-registry results to a specific state (e.g. "on", "off", "unavailable"). Case-insensitive. Can be used standalone (no query/domain/area) to enumerate every entity in that state; entity_total_matches reflects the filtered count. | |
| domain_filter | No | Narrow entity-registry results to a single domain (e.g. 'light', 'sensor'). | |
| result_fields | No | 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. | |
| include_config | No | Include full configuration bodies in body-search results. Otherwise summaries only. | |
| include_hidden | No | Include hidden entities in registry results (with a score penalty so they sort below visible matches). Set False to exclude entirely. | |
| group_by_domain | No | Group entity-registry results by domain (entity-side only). Adds a `by_domain` map to the response. | |
| per_domain_limit | No | When `group_by_domain=True`, cap entity-registry results per domain to this number. Ignored otherwise. | |
| config_time_budget | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so safety is covered; the description adds almost nothing beyond that. It does not mention partial/error responses, the per-surface pagination model, or the score penalty applied to hidden entities โ all behaviors disclosed only inside the schema parameter docs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first sentence is well front-loaded and wastes nothing. The second line is a bare keyword dump that largely repeats the parenthetical already in sentence one and reads as retrieval bait rather than information an agent needs to call the tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema, full annotations and 100% schema coverage, the description only has to frame the call โ and it does frame the two search surfaces. But for a 15-parameter tool with mode-switching semantics (registry-listing vs config-body vs config-only), the prose is too thin to stand alone without reading the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% (15 parameters, every one documented in detail including modes, defaults and interactions), so the baseline is 3. The description itself contributes no parameter meaning; the trailing token list (lights, sensors, switches, climate...) loosely hints at domain_filter values but does not explain any argument.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb (Search) and two distinct resources โ the entity registry and automation/script/scene/helper/dashboard config bodies โ so an agent knows this is a dual-surface lookup, not a single-object getter. It stops short of naming a sibling (e.g. ha_get_entity or ha_config_list_helpers) as the alternative, so the boundary versus neighbors is left to inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description conveys the two usage modes ('in one call' covering entities AND configurations) but never says when to prefer this over ha_get_entity, ha_config_list_helpers, or ha_list_floors_areas. The critical rule that passing search_types skips entity search lives only in the schema parameter text, not in the description an agent reads first.
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 FloorADestructive
Create or update a Home Assistant area or floor.
Pass kind='area' (with optional floor_id, picture, labels, temperature_entity_id, humidity_entity_id) 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="area", id="kitchen", temperature_entity_id="sensor.kitchen_temp", humidity_entity_id=None) 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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | Existing area_id or floor_id to update (use ha_list_floors_areas to find IDs) | |
| icon | No | Material Design Icon (e.g., 'mdi:sofa', 'mdi:home-floor-1'; null or empty string to remove; omit to leave unchanged) | |
| kind | Yes | Which registry to operate on: 'area' for rooms, 'floor' for building levels | |
| name | No | Name (required when creating; optional when updating, e.g., 'Living Room', 'Ground Floor') | |
| level | No | Numeric level when kind='floor' (0=ground, 1=first, -1=basement). | |
| labels | No | Label IDs when kind='area' (replaces the area's label set; empty list to clear). Omit to leave labels unchanged. Floors have no labels. | |
| aliases | No | Alternative names for voice assistant recognition (e.g., ['lounge'], empty list to clear) | |
| picture | No | Picture URL when kind='area' (null or empty string to remove; omit to leave unchanged). | |
| floor_id | No | Floor assignment when kind='area' (null or empty string to clear; omit to leave unchanged). | |
| humidity_entity_id | No | Sensor the area reports humidity from, when kind='area' (a sensor.* entity with device_class humidity; null or empty string to clear). Omit to leave unchanged. | |
| temperature_entity_id | No | Sensor the area reports temperature from, when kind='area' (a sensor.* entity with device_class temperature; null or empty string to clear). Omit to leave unchanged. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true and idempotentHint=false, and the description adds genuinely useful behavior beyond them: the create-vs-update branch, per-kind parameter rejection with a concrete error code, and the Claude Desktop timeout/repeat-read workaround. It stops short of explaining that null/empty-string values clear fields or what permissions are needed, which matters for a destructive write.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose, the kind split, and the create/update rule are front-loaded and the examples earn their space. The trailing Claude Desktop timeout paragraph is long and somewhat tangential, blunting an otherwise tight definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 create/update branch, both kinds, cross-kind errors, and worked examples for an 11-parameter mutation tool. The remaining gap is the destructive/overwrite implications of passing null or empty-string values, which is never surfaced despite destructiveHint=true.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the per-parameter descriptions already carry the documented semantics (baseline 3). The description adds value by consolidating which optional parameters belong to kind='area' vs kind='floor' and by showing six concrete call shapes, which is more than the schema alone conveys.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a precise verb pair and resource ('Create or update a Home Assistant area or floor') and immediately splits the resource into its two kinds with their applicable parameters. An agent can distinguish this from ha_remove_area_or_floor and ha_list_floors_areas 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit decision rule ('Provide name only to create a new entry; provide id to update an existing one') and states that cross-kind parameters are rejected with VALIDATION_INVALID_PARAMETER, which is real routing guidance. It does not, however, name alternative tools for the lookup step or say when not to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_set_deviceSet DeviceADestructiveIdempotent
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:
ha_set_device(device_id="abc", name="Living Room Sensor") # Rename device
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | New display name for the device (sets name_by_user). '' clears name_by_user and reverts to the integration-provided name. A whitespace-only value acts like '', for clients that cannot send an empty string. | |
| labels | No | Labels to assign to the device (replaces existing labels) | |
| area_id | No | Area/room ID to assign the device to. Use empty string '' to unassign. A whitespace-only value acts like '', for clients that cannot send an empty string. | |
| device_id | Yes | Device ID to update | |
| disabled_by | No | Set to 'user' to disable the device, or '' (empty string) to enable it. Omit to leave the disabled state unchanged. A whitespace-only value acts like '', for clients that cannot send an empty string. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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 genuinely non-derivable behavior: the device/entity name independence gotcha and a client-specific timeout workaround that a caller could not infer from schema or annotations. It could still say more about permissions or reversibility, but the added context is substantive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose, then an IMPORTANT gotcha, a workflow, and examples โ a clean progression. The closing Claude Desktop timeout paragraph is useful but somewhat tangential and lengthens the definition, slightly diluting the otherwise tight structure.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 explained, and the description covers the key mutation gotcha, per-parameter behavior, and invocation examples. Nothing essential to calling it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 improves on it by giving concrete invocation examples (disabled_by='user' to disable, '' to enable, labels=[...]) that demonstrate the empty-string conventions in practice. It does not add semantics beyond what the schema already documents in detail, keeping it just above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Update device properties') and enumerates exactly what is mutable (name, area, disabled state, labels). It explicitly distinguishes itself from the sibling ha_set_entity, warning that renaming a device does not rename its entities.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternative tool (ha_set_entity) and the precise condition under which it is needed, then supplies a numbered workflow showing the two calls chained for a full rename. Concrete invocation examples cover each parameter case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_set_entitySet EntityADestructiveIdempotent
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, use_device_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(entity_id="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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| icon | No | Icon for the entity (e.g., 'mdi:thermometer'). Use empty string '' to remove custom icon. A whitespace-only value acts like '', for clients that cannot send an empty string. | |
| name | No | Display name for the entity. Use empty string '' to remove custom name and revert to default. A whitespace-only value acts like '', for clients that cannot send an empty string. | |
| hidden | No | True to hide the entity from UI, False to show it. | |
| labels | No | List of label IDs for the entity. Behavior depends on label_operation parameter. Use [] with label_operation='set' to clear. | |
| aliases | No | 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. | |
| area_id | No | Area/room ID to assign the entity to. Use empty string '' to unassign from current area. A whitespace-only value acts like '', for clients that cannot send an empty string. | |
| enabled | No | 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. | |
| options | No | 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. | |
| entity_id | Yes | Entity ID or list of entity IDs to update. | |
| expose_to | No | 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}. | |
| categories | No | Category assignment as a dict mapping scope to category_id. Example: {"automation": "category_id_here"}. Use null value to clear: {"automation": null}. | |
| device_class | No | 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. A whitespace-only value acts like '', for clients that cannot send an empty string. | |
| new_entity_id | No | New entity ID to rename to (e.g., 'light.new_name'). Domain must match the original. | |
| label_operation | No | How to apply labels: 'set' replaces all labels, 'add' adds to existing, 'remove' removes specified labels. | set |
| new_device_name | No | New display name for the associated device. If provided, both entity and device are updated in one operation. Empty or whitespace-only never changes the device name: it is reported under warnings when other fields are given and is an error when it is the only update. To clear the device name use ha_set_device(name=''). | |
| use_device_name | No | HA 2026.10's 'Use device name' switch. True names the entity after its device and follows device renames; False restores the integration's default name. Omit to leave it as is; not combinable with name. | |
| use_entity_name_alias | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag this as a destructive, idempotent write, but the description adds substantial behavior beyond that: renaming does not propagate to automations, scripts, templates, or dashboards; history is preserved (HA 2022.4+) but entities without unique IDs or disabled by their integration cannot be renamed; enabled=False removes the entity from the state machine and requires a reload; device_class changes apply instantly; options are sent as separate per-domain registry updates. These are non-obvious operational consequences an agent could not infer from the 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Headers (BULK OPERATIONS, ENTITY ID RENAME, ENABLED/DISABLED WARNING, EXAMPLES) make it skimmable and the critical rename warning is front-loaded in caps. Some content is redundant, especially the Claude Desktop timeout workaround paragraph, which is client-specific operational advice that inflates length without helping tool selection or invocation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 17 parameters, destructive semantics, and a rename capability, the description covers the risky paths thoroughly: bulk restrictions, rename caveats, the enabled/disabled trap, and correct alternatives for automations and scripts. An output schema exists, so return-value explanation is correctly omitted.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 genuine cross-parameter semantics the schema does not express: which parameters are invalid in bulk mode, the fact that new_device_name combines with new_entity_id in one call, the note that use_device_name is not combinable with name, and the mapping of device_class to the UI's 'Show As'. It stops short of fully systematizing these constraints, so it is not a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Update entity properties in the entity registry') and enumerates the modifiable fields, including area, name, icon, device class, options, enabled state, aliases, labels, exposure, and entity_id rename. This clearly separates it from ha_set_device, ha_get_entity, and ha_remove_entity, which operate on different resources.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent elsewhere when needed: use ha_search()/ha_get_device() to find entity IDs, ha_config_get_label() for label IDs, and ha_config_set_automation(entity_id=..., enabled=False) instead of enabled=False on automations. It also draws the bulk-vs-single boundary (only labels, expose_to, categories work on lists) and notes ha_set_device(name='') for clearing a device name, so the agent knows both when to use and when not to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_set_integrationSet IntegrationADestructive
Manage an integration (config entry): enable/disable, add, update options, reconfigure, or send its own commands.
Modes (pick one):
Enable/disable: entry_id + enabled.
Add integration: domain (+ menu_choice, + config) โ runs the domain's config flow one step per call, as the HA UI does.
Update options: entry_id (+ menu_choice, + config) โ the entry's options flow (what the "Configure" button does in the HA UI), one step per call.
Reconfigure: entry_id + reconfigure=True โ connection settings such as host, port, credentials, one step per call. Each call except an abort first checks the entry's identity; the first call without config returns the flow's first step.
Log level: log_level + domain (or entry_id) โ sets how much the integration logs; read it back with ha_get_integration's log_level.
Integration command: entry_id + ws_command (+ ws_data) โ what the integration's panel sends (KNX project, entities, telegrams). Home Assistant marks none of these commands as read-only, so a read such as knx/get_knx_project goes through this tool too.
Flows, one step per call: a call answers the step the flow is at and returns Home Assistant's next answer. A form comes back with its fields (type, default, current value, Home Assistant's help text) and flow_id; a menu with menu_options. Answer with flow_id and config (a form) or menu_choice (a menu); flow_id alone returns the step again, and flow_id with abort=True ends the flow. Without flow_id a new flow starts: its first form gets config, and without config the call returns that first step. A form Home Assistant rejects is an error with errors and error_text (Core's text), and the flow stays open to correct it. A flow stays open until Home Assistant finishes it or you abort it; while a setup flow is open, Home Assistant refuses another one for the same device as already_in_progress.
WHEN NOT TO USE:
Helpers (template, group, utility_meter, ...): use ha_config_set_helper.
otpis the user's to set up in the HA UI: its secret is a credential enrolled in an authenticator app; do not create it.Config subentries: use ha_config_set_helper(helper_type='config_subentry'); subentry_type here describes their form; a menu_choice that finishes the flow saves the subentry.
Removing an entry: use ha_remove_helpers_integrations.
ha-mcp's own ha_mcp_tools entries are refused: manage them in the HA UI, or (server settings, developer mode on) ha_dev_manage_settings / ha_dev_manage_server.
Use ha_get_integration() to find entry IDs, and ha_get_integration(entry_id=..., include_schema=True) to inspect the options fields. Its supports_reconfigure field tells you whether an entry qualifies for reconfigure=True, and ha_get_integration(entry_id=..., include_ws_commands=True) lists the commands ws_command accepts with their schemas.
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) are refused at that step and aborted. A progress step (Home Assistant working in the background) is waited out for up to 120 s; after that the error carries flow_id to ask again. When Home Assistant does not answer a submit or a progress poll (or, on reconfigure, answers with any 5xx), the error carries status applied_but_unverified: the entry may exist or the change may be saved, so check it before retrying. An aborted flow returns Core's own text next to the key (reason_text). Reconfigure edits the settings a live integration connects with: a wrong host or credential takes it offline, and there is no automatic rollback. A finished reconfigure waits for the reload Home Assistant runs and reports the entry's state as entry_state: status applied_and_verified once it is loaded, applied_but_unverified otherwise.
EXAMPLES:
Add: ha_set_integration(domain="local_calendar", config={"calendar_name": "Chores"})
Add in two steps: ha_set_integration(domain="knx", config={"connection_type": "routing"}) returns the routing form and flow_id, then ha_set_integration(domain="knx", flow_id="...", config={})
Answer a menu: ha_set_integration(domain="...", flow_id="...", menu_choice="")
See the setup form first: ha_set_integration(domain="workday")
Update options: ha_set_integration(entry_id="abc123", config={"scan_interval": 30})
Reconfigure: ha_set_integration(entry_id="abc123", reconfigure=True), then ha_set_integration(entry_id="abc123", reconfigure=True, flow_id="...", config={"host": "10.0.0.5"})
Cancel a flow: ha_set_integration(domain="knx", flow_id="...", abort=True)
Debug logging: ha_set_integration(domain="zha", log_level="DEBUG"), then log_level="DEFAULT" to stop
KNX project: ha_set_integration(entry_id="abc123", ws_command="knx/get_knx_project")
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
integration config entry enable disable add options reconfigure log level debug logging
| Name | Required | Description | Default |
|---|---|---|---|
| abort | No | With flow_id: end the open flow without saving. A flow Home Assistant no longer has, or one ha-mcp did not open (or opened before it restarted), is an error. | |
| config | No | The values for the form the flow is at: one form per call. Without flow_id it answers the new flow's first form (after menu_choice, when the flow opens with a menu). A key the form does not declare is refused before anything is sent. On an existing entry (options, reconfigure) 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. | |
| domain | No | Integration domain to add (e.g. 'workday', 'local_calendar'). | |
| enabled | No | True to enable, False to disable the entry. Mutually exclusive with 'domain' and 'config'. | |
| flow_id | No | Continue an open flow: the flow_id a previous call returned, with the same domain or entry_id (and reconfigure=True for a reconfigure flow), until ha-mcp restarts. With config or menu_choice it answers the step; alone it returns the step again. | |
| ws_data | No | The ws_command's arguments, without 'type' and 'id'. Home Assistant checks them against the command's registered schema and returns its error when they do not match. | |
| entry_id | No | Config entry ID of an existing integration. | |
| log_level | No | 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 no other mode's parameters (log_persistence may accompany it). Lasts through the next Home Assistant restart unless log_persistence says otherwise. 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. | |
| ws_command | No | With entry_id: one of the integration's own '<domain>/' WebSocket commands to send, as its panel in the HA UI does (e.g. 'knx/get_knx_project'). Its result is returned under result. Streaming commands are refused. | |
| menu_choice | No | The option to pick at a menu step: with flow_id the menu the flow is at, without it the menu a new flow opens with. Refused when the step is not a menu or does not offer the option. | |
| reconfigure | No | Use the existing config entry's official reconfigure flow (its connection settings) instead of its options flow. Every call except an abort checks the entry's identity first. Without flow_id and config this opens the flow and returns its first step; nothing is submitted. Continue with the flow_id this entry's call returned: Home Assistant does not say which entry a reconfigure flow edits. | |
| subentry_id | No | With entry_id and subentry_type: the existing subentry whose reconfigure form to describe. | |
| expected_mac | No | Requires reconfigure=True. MAC or IEEE the entry's device must still report. | |
| subentry_type | No | With entry_id: the integration-defined subentry type whose form to describe as subentry_schema; the flow is aborted afterwards, unless a menu_choice finished it (flow_type create_entry: saved). | |
| log_persistence | No | Only with log_level: how long Home Assistant keeps the level. 'once' (default) through the next restart, 'permanent' after every restart until changed, 'none' until the next restart. Home Assistant validates the value. | |
| expected_device_id | No | Requires reconfigure=True. Device registry ID the entry must still own. | |
| expected_unique_id | No | 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. | |
| expected_entity_ids | No | Requires reconfigure=True. Exact entity IDs that must remain associated with the entry. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare destructiveHint=true and readOnlyHint=false; the description adds far more: OAuth steps are refused and aborted, progress steps wait up to 120s, ambiguous responses return status applied_but_unverified, reconfigure has no automatic rollback and can take a live integration offline, and a finished reconfigure reports entry_state. It also documents the one-step-per-call flow lifecycle and the already_in_progress refusal, none of which the annotations convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Structure is strong: purpose first, then modes, exclusions, flow semantics, caveats, and worked examples, all clearly delimited. It is nonetheless very long for a tool whose 18 parameters are already 100% documented, and the trailing Claude Desktop timeout workaround is tangential padding rather than tool semantics.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a highly complex, multi-mode, stateful flow-driving mutation tool, the description covers everything an agent needs: mode selection, flow continuation/abort, error shapes (errors/error_text, reason_text), verification states, and prerequisites for discovery. Return values are covered by the output schema and correctly not duplicated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema coverage the baseline is 3, but the description earns above that by mapping parameter combinations to modes (entry_id+enabled vs domain+config vs entry_id+reconfigure=True vs log_level+domain) and showing the ordering of flow_id across calls, which the schema only states piecewise. Some of this overlaps with schema descriptions (mutual exclusivity, 'Requires reconfigure=True'), so it is not fully additive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening line states a specific verb+resource ('Manage an integration (config entry)') and immediately enumerates the concrete operations: enable/disable, add, update options, reconfigure, send commands. It explicitly names the sibling tools it is not (ha_config_set_helper, ha_remove_helpers_integrations, ha_get_integration), so an agent can distinguish it 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is a dedicated 'WHEN NOT TO USE' block routing helpers, otp, config subentries, entry removal, and ha_mcp_tools entries to their correct tools, plus a per-mode parameter recipe ('Modes (pick one)') that tells the agent exactly which arguments select which behavior. It also points to ha_get_integration for discovery and include_schema/include_ws_commands for inspection.
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 ItemADestructive
Create, update or reorder a todo item in Home Assistant.
Without item (create mode): creates a new item from summary.
With item (update mode): changes the fields given, and moves the item
when move_after is given, alone or together with other changes.
Home Assistant refuses a description or due date the list does not
support, and says which. The reply carries todo_item, the item as
Home Assistant stores it afterwards, uid included, or null with a
warning when it cannot be read back. With auto-backup on, an updated
item is backed up first; warnings names a backup that was not taken
because the item could not be read or the backup could not start.
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")
Move to top: ha_set_todo_item("todo.tasks", item="Pay bills", move_after="")
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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| item | No | Existing item to update - can be the item UID or the exact item summary/name. | |
| rename | No | New name/summary for an existing item. | |
| status | No | Item status: 'completed' to mark done, 'needs_action' to mark incomplete. Update mode only: to add a done item, create it, then update it. | |
| summary | No | Item text/name. Ignored in update mode โ use 'rename' to change the item name. | |
| due_date | No | Due date in YYYY-MM-DD format (e.g., '2024-12-25'). An empty string clears the due date on update. | |
| entity_id | Yes | Todo list entity ID (e.g., 'todo.shopping_list') | |
| move_after | No | Move the item directly below this item (UID or exact summary). An empty string moves it to the top of the list. | |
| description | No | Detailed description for the item. An empty string clears it on update. | |
| due_datetime | No | Due datetime in ISO format (e.g., '2024-12-25T14:00:00'). An empty string clears the due date on update. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (destructiveHint=true, idempotentHint=false), the description discloses that Home Assistant rejects unsupported description/due-date fields and reports which, that the reply carries `todo_item` or null plus a warning, and that auto-backup runs first with `warnings` naming a skipped backup. The gap is that nothing explains what makes the call destructive (e.g. whether omitted fields are cleared) despite destructiveHint=true.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and mode rules are front-loaded and the examples are compact. However, the closing Claude Desktop timeout paragraph is lengthy, tangential to what the tool does, and reads as client-specific operational advice rather than tool semantics, which dilutes the otherwise tight definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description need not document return shape, yet it still explains the `todo_item`/null-plus-warning outcome, which is helpful. For a 9-parameter mutation tool with destructiveHint=true, the missing piece is a statement of what state is overwritten in update mode.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 cross-parameter meaning the schema cannot: `item` acts as a mode switch, and `move_after` is described as usable alone or alongside other changes. That mode/parameter interaction is genuine added value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb set (create, update, reorder) and resource (a todo item in Home Assistant), and the create-mode/update-mode split clarifies the two behaviours behind one tool. It does not name sibling tools such as ha_get_todo or ha_remove_todo_item, so the boundary against those is inferred from the names alone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit branching guidance: absent `item` means create, present `item` means update, and `move_after` can be combined with other changes or used alone. The worked examples make each mode concrete, though there are no explicit exclusions pointing to ha_remove_todo_item for deletion or ha_get_todo for reading.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_set_zoneSet ZoneADestructive
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). Zones defined in YAML, and the home zone, cannot be changed 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="office", radius=200)
Update by entity_id: ha_set_zone(zone_id="zone.office", 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, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| icon | No | Material Design Icon (e.g., 'mdi:briefcase'). On update, '' clears it, except an icon stored in the zone itself (as the Home Assistant UI stores it; ha_get_zone shows it), which cannot be removed | |
| name | No | Display name for the zone | |
| wait | No | Wait for the zone to be queryable before returning. Set to False for bulk operations. | |
| radius | No | Radius of the zone in meters; Home Assistant uses 100 when it is omitted on create | |
| passive | No | Passive mode: the zone is hidden in the frontend and not used for device tracker state, but automations can still use it (false when omitted on create) | |
| zone_id | No | Zone ID or entity_id of the zone to update (from ha_get_zone) | |
| latitude | No | Latitude coordinate of the zone center | |
| longitude | No | Longitude coordinate of the zone center |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=false, so the description's real contribution is the partial-update semantics ('only specified fields change') and the immutability of YAML/home zones. The Claude Desktop timeout-and-hang workaround is unusually specific operational context that cannot be inferred from the schema. It stops short of saying what happens to conflicting existing values or permissions needed, hence 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the create/update branching and limitations, followed by three compact, immediately usable examples. The final Claude Desktop paragraph is long and slightly superstitious in tone, but it is distinct operational guidance rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema, full annotation coverage, and 100% parameter description coverage, the description only needs to supply semantics and routing, which it does: create vs update, field-level partial semantics, immutability constraints, and examples. Nothing an agent needs in order 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3; the description still earns credit by naming which params are required on create and by showing worked examples for radius/icon and for passing either a zone_id or an entity_id form. It does not add anything about `wait` or `passive` beyond the schema, so it is above baseline but not exhaustive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource (create/update a Home Assistant zone) with the create-vs-update branch spelled out in the first two lines. It also carves out scope exclusions (YAML-defined zones and the home zone), which no sibling like ha_get_zone or ha_remove_zone covers, so an agent can select it unambiguously.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit rule for the two modes: omit zone_id to create (with required fields named), provide zone_id to update (only specified fields change). It also states a hard when-not: zones defined in YAML and the home zone cannot be modified here, which prevents a whole class of misuse.
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.
4 tool updates
v9.1.1- Changed
ha_config_list_helpers1 field changed- changed
Input schema / properties / menu_choice / descriptionPrevious value: -"describe only: sub-type of a menu-based helper (template, group), e.g. 'sensor'."New value: +"describe only: sub-type of a menu-based helper (template, group), e.g. 'sensor'. A choice that finishes the flow saves the helper: flow_type create_entry."
- Changed
ha_get_integration6 fields changed- removed
Input schema / properties / include_knx_projectRemoved value: -{ - "default": false, - "description": "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.", - "type": "boolean" -} - changed
Input schema / properties / include_options / descriptionPrevious value: -"Include 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."New value: +"Include 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 here. Read through the ha_mcp_tools custom component; without it an entry that has an options flow comes back without options and a warning, and only include_schema on that entry reads them. With the component, prefer this over include_schema when you only need the current values; use include_schema when you also need the field types or selector metadata." - changed
Input schema / properties / include_schema / descriptionPrevious value: -"When entry_id is set, also return the options flow schema (available fields and their types). Only applies when supports_options=true."New value: +"When entry_id is set, also describe the options flow form as options_schema.fields: each field's type, current value and Home Assistant's help text. A flow that opens with a menu lists menu_options instead (ha_set_integration(entry_id=..., menu_choice=...) opens one), and a flow Home Assistant aborts gives aborted, plus reason_text when Home Assistant has a string for the reason. Only applies when supports_options=true. The flow is started, described and aborted; starting it runs the integration's own flow code, and some integrations act there (Sonoff LAN signs in to its cloud). For an integration's setup form, use ha_set_integration(domain=...)." - changed
Input schema / properties / include_subentry_schema / descriptionPrevious value: -"When 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."New value: +"When entry_id is set, describe a config subentry flow's form as subentry_schema.fields (same shape as include_schema); the flow is aborted afterwards. Pair with subentry_type, and optionally subentry_id for the reconfigure form." - added
Input schema / properties / include_ws_commandsAdded value: +{ + "default": false, + "description": "When entry_id is set, also list the integration's own '<domain>/' WebSocket commands (the API its panel uses, e.g. KNX's knx/get_knx_project) under ws_commands, each with the request schema Home Assistant registered for it. Send one with ha_set_integration(entry_id=..., ws_command=...). Needs the ha_mcp_tools custom component; without it a warning says the list is unavailable.", + "type": "boolean" +} - added
Input schema / properties / integration_typeAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "When listing, keep only entries whose integration has this type, matched by Home Assistant itself (the manifest's integration_type, e.g. 'device', 'hub', 'service', 'helper'). Home Assistant does not reject an unknown type: for any type but 'helper' it also keeps entries whose integration it cannot find (e.g. a removed custom integration), so an unknown type returns only those." +}
- Changed
ha_manage_hacs3 fields changed- changed
Input schema / properties / action / descriptionPrevious 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"New value: +"'download' to install/update, 'add_repository' to register a custom repo, 'remove' to uninstall a downloaded repo, 'update_information' to refresh a repository's release data from GitHub, or 'accept_warning' to accept the Marketplace's first-run warning for the user this server connects as" - changed
Input schema / properties / action / enumPrevious value: -[ - "download", - "add_repository", - "remove", - "update_information" -]New value: +[ + "download", + "add_repository", + "remove", + "update_information", + "accept_warning" +] - added
Input schema / properties / confirmAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "action='accept_warning': True once the user has said they read the Marketplace's first-run warning and accept it" +}
- Changed
ha_set_integration13 fields changed- added
Input schema / properties / abortAdded value: +{ + "default": false, + "description": "With flow_id: end the open flow without saving. A flow Home Assistant no longer has, or one ha-mcp did not open (or opened before it restarted), is an error.", + "type": "boolean" +} - changed
Input schema / properties / config / descriptionPrevious 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."New value: +"The values for the form the flow is at: one form per call. Without flow_id it answers the new flow's first form (after menu_choice, when the flow opens with a menu). A key the form does not declare is refused before anything is sent. On an existing entry (options, reconfigure) 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." - removed
Input schema / properties / confirm_tokenRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "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." -} - changed
Input schema / properties / expected_device_id / descriptionPrevious value: -"Requires reconfigure=True. Device registry ID the entry must still own, before and after the change."New value: +"Requires reconfigure=True. Device registry ID the entry must still own." - added
Input schema / properties / flow_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Continue an open flow: the flow_id a previous call returned, with the same domain or entry_id (and reconfigure=True for a reconfigure flow), until ha-mcp restarts. With config or menu_choice it answers the step; alone it returns the step again." +} - changed
Input schema / properties / log_level / descriptionPrevious value: -"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."New value: +"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 no other mode's parameters (log_persistence may accompany it). Lasts through the next Home Assistant restart unless log_persistence says otherwise. 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." - added
Input schema / properties / log_persistenceAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Only with log_level: how long Home Assistant keeps the level. 'once' (default) through the next restart, 'permanent' after every restart until changed, 'none' until the next restart. Home Assistant validates the value." +} - added
Input schema / properties / menu_choiceAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The option to pick at a menu step: with flow_id the menu the flow is at, without it the menu a new flow opens with. Refused when the step is not a menu or does not offer the option." +} - changed
Input schema / properties / reconfigure / descriptionPrevious value: -"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."New value: +"Use the existing config entry's official reconfigure flow (its connection settings) instead of its options flow. Every call except an abort checks the entry's identity first. Without flow_id and config this opens the flow and returns its first step; nothing is submitted. Continue with the flow_id this entry's call returned: Home Assistant does not say which entry a reconfigure flow edits." - added
Input schema / properties / subentry_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "With entry_id and subentry_type: the existing subentry whose reconfigure form to describe." +} - added
Input schema / properties / subentry_typeAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "With entry_id: the integration-defined subentry type whose form to describe as subentry_schema; the flow is aborted afterwards, unless a menu_choice finished it (flow_type create_entry: saved)." +} - added
Input schema / properties / ws_commandAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "With entry_id: one of the integration's own '<domain>/' WebSocket commands to send, as its panel in the HA UI does (e.g. 'knx/get_knx_project'). Its result is returned under result. Streaming commands are refused." +} - added
Input schema / properties / ws_dataAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The ws_command's arguments, without 'type' and 'id'. Home Assistant checks them against the command's registered schema and returns its error when they do not match." +}
11 tool updates
v9.1.0- Changed
ha_config_get_automation3 fields changed- added
Input schema / properties / entity_idAdded value: +{ + "description": "The automation's entity_id (e.g. 'automation.morning_routine') or its config id", + "type": "string" +} - removed
Input schema / properties / identifierRemoved value: -{ - "description": "Automation entity_id (e.g., 'automation.morning_routine') or unique_id", - "type": "string" -} - changed
Input schema / requiredPrevious value: -[ - "identifier" -]New value: +[ + "entity_id" +]
- Changed
ha_config_list_dashboard_resources1 field changed- added
Input schema / properties / include_hashAdded value: +{ + "default": false, + "description": "Add \"_config_hash\" (SHA-256 hex digest of the decoded UTF-8 content) to each inline resource, e.g. to verify an edit or to pass as config_hash without fetching the content.", + "type": "boolean" +}
- Changed
ha_config_remove_automation3 fields changed- added
Input schema / properties / entity_idAdded value: +{ + "description": "The automation's entity_id (e.g. 'automation.old_automation') or its config id", + "type": "string" +} - removed
Input schema / properties / identifierRemoved value: -{ - "description": "Automation entity_id (e.g., 'automation.old_automation') or unique_id to delete", - "type": "string" -} - changed
Input schema / requiredPrevious value: -[ - "identifier" -]New value: +[ + "entity_id" +]
- Changed
ha_config_set_automation6 fields changed- changed
Input schema / properties / config / descriptionPrevious 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."New value: +"Complete automation configuration, written as given. Purpose-specific triggers/conditions (HA 2026.7+ default: 'trigger': '<domain>.<name>' with 'target'/'options') are valid config. Mutually exclusive with python_transform." - added
Input schema / properties / entity_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The automation's entity_id (automation.x) or its config id; omit to create. An unused config id creates the automation under that id; values such as 'new' are literal ids, not placeholders." +} - removed
Input schema / properties / identifierRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "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." -} - changed
Input schema / properties / python_transform / descriptionPrevious 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"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, index, count, sort,\n reverse\n- Dict methods: update, get, setdefault, keys, values, items, pop, clear\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, lstrip, rstrip,\n removeprefix, removesuffix, split, join, replace, count, index\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" - changed
Input schema / properties / run_actions / descriptionPrevious value: -"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."New value: +"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 entity_id and no config." - changed
Input schema / properties / take_control_of_blueprint / descriptionPrevious 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."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 entity_id; mutually exclusive with config and python_transform."
- Changed
ha_config_set_dashboard2 fields changed- changed
Input schema / properties / python_transform / descriptionPrevious 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"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, index, count, sort,\n reverse\n- Dict methods: update, get, setdefault, keys, values, items, pop, clear\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, lstrip, rstrip,\n removeprefix, removesuffix, split, join, replace, count, index\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" - changed
Input schema / properties / title / descriptionPrevious value: -"Dashboard display name shown in sidebar"New value: +"Sidebar name; required when creating a dashboard"
- Changed
ha_config_set_dashboard_resource2 fields changed- added
Input schema / properties / config_hashAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "With edits: refuse the patch unless the stored content has this hash when read, just before the write (the list's _config_hash with include_hash=True, or a previous write response's config_hash). A preflight check only: a change made by another client after that read is not detected and can be overwritten." +} - added
Input schema / properties / editsAdded value: +{ + "anyOf": [ + { + "items": { + "additionalProperties": false, + "description": "One exact-match replacement in an inline resource.", + "properties": { + "new": { + "description": "Replacement text (may be empty).", + "type": "string" + }, + "old": { + "description": "Exact text to replace. Must occur exactly once in the content at the point this edit is applied; include surrounding context to make it unique.", + "type": "string" + } + }, + "required": [ + "old", + "new" + ], + "type": "object" + }, + "maxItems": 100, + "minItems": 1, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Search/replace edits for an existing inline resource, applied in order. Requires resource_id; resource_type is ignored and the stored type is kept." +}
- Changed
ha_config_set_scene1 field changed- changed
Input schema / properties / python_transform / descriptionPrevious 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"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, index, count, sort,\n reverse\n- Dict methods: update, get, setdefault, keys, values, items, pop, clear\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, lstrip, rstrip,\n removeprefix, removesuffix, split, join, replace, count, index\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"
- Changed
ha_config_set_script1 field changed- changed
Input schema / properties / python_transform / descriptionPrevious 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"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, index, count, sort,\n reverse\n- Dict methods: update, get, setdefault, keys, values, items, pop, clear\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, lstrip, rstrip,\n removeprefix, removesuffix, split, join, replace, count, index\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"
- Changed
ha_get_todo2 fields changed- changed
Input schema / properties / status / anyOfPrevious value: -[ - { - "enum": [ - "needs_action", - "completed" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +] - changed
Input schema / properties / status / descriptionPrevious value: -"Filter items by status: 'needs_action' for incomplete, 'completed' for done. Only applies when entity_id is provided."New value: +"Only items with this status, or with any of a list of statuses: 'needs_action' (open) or 'completed' (done). Only applies when entity_id is provided."
- Changed
ha_remove_todo_item6 fields changed- added
Input schema / properties / item / anyOfAdded value: +[ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } +] - added
Input schema / properties / item / defaultAdded value: +null - changed
Input schema / properties / item / descriptionPrevious value: -"Item to remove - can be the item UID (from ha_get_todo) or the exact item summary/name"New value: +"Item to remove, or a list of items - each the item UID (from ha_get_todo) or the exact item summary/name. A string that names no item but holds a JSON array of strings is that list." - removed
Input schema / properties / item / typeRemoved value: -"string" - added
Input schema / properties / remove_completedAdded value: +{ + "default": false, + "description": "Remove every completed item", + "type": "boolean" +} - changed
Input schema / requiredPrevious value: -[ - "entity_id", - "item" -]New value: +[ + "entity_id" +]
- Changed
ha_set_todo_item7 fields changed- changed
Input schema / properties / description / descriptionPrevious value: -"Detailed description for the item"New value: +"Detailed description for the item. An empty string clears it on update." - changed
Input schema / properties / due_date / descriptionPrevious value: -"Due date in YYYY-MM-DD format (e.g., '2024-12-25')"New value: +"Due date in YYYY-MM-DD format (e.g., '2024-12-25'). An empty string clears the due date on update." - changed
Input schema / properties / due_datetime / descriptionPrevious value: -"Due datetime in ISO format (e.g., '2024-12-25T14:00:00'). Overrides due_date if both provided."New value: +"Due datetime in ISO format (e.g., '2024-12-25T14:00:00'). An empty string clears the due date on update." - added
Input schema / properties / move_afterAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Move the item directly below this item (UID or exact summary). An empty string moves it to the top of the list." +} - changed
Input schema / properties / rename / descriptionPrevious value: -"New name/summary for an existing item. Only used in update mode."New value: +"New name/summary for an existing item." - changed
Input schema / properties / status / anyOfPrevious value: -[ - { - "enum": [ - "needs_action", - "completed" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / status / descriptionPrevious value: -"Item status: 'completed' to mark done, 'needs_action' to mark incomplete. Only used in update mode."New value: +"Item status: 'completed' to mark done, 'needs_action' to mark incomplete. Update mode only: to add a done item, create it, then update it."
18 tool updates
v9.0.0- Changed
ha_config_get_calendar_events1 field changed- changed
Input schema / properties / start / descriptionPrevious value: -"Start datetime in ISO format (default: now)"New value: +"Start datetime in ISO format; omit it to start at the current time"
- Changed
ha_config_remove_group1 field changed- changed
Input schema / properties / object_id / descriptionPrevious value: -"Group identifier without 'group.' prefix (e.g., 'living_room_lights')"New value: +"Group identifier: a lowercase slug without the 'group.' prefix (e.g., 'living_room_lights')"
- Changed
ha_config_set_dashboard2 fields changed- changed
Input schema / properties / icon / descriptionPrevious value: -"MDI icon name (e.g., 'mdi:home', 'mdi:cellphone'). Defaults to 'mdi:view-dashboard'"New value: +"MDI icon name (e.g., 'mdi:home', 'mdi:cellphone'). Omit to store none (the sidebar then shows mdi:view-dashboard); '' clears a stored icon." - changed
Input schema / properties / url_path / descriptionPrevious value: -"Dashboard URL path (e.g., 'my-dashboard'). Use 'default' or 'lovelace' for the default dashboard. New dashboards must use a hyphenated path."New value: +"Dashboard URL path (e.g., 'my-dashboard'). Use 'default' or 'lovelace' for the default dashboard. Home Assistant requires a hyphen in new dashboard paths."
- Changed
ha_config_set_dashboard_resource2 fields changed- changed
Input schema / properties / resource_type / descriptionPrevious value: -"Resource type: 'module' for ES6 modules (modern cards), 'js' for legacy JavaScript (older custom cards), 'css' for stylesheets (themes, global styles)"New value: +"Resource type, validated by Home Assistant: 'module' for ES6 modules (modern cards), 'js' for legacy JavaScript (older custom cards), 'css' for stylesheets (themes, global styles), 'html' for HTML imports" - removed
Input schema / properties / resource_type / enumRemoved value: -[ - "module", - "js", - "css" -]
- Changed
ha_config_set_group6 fields changed- changed
Input schema / properties / add_entities / descriptionPrevious value: -"Add these entities to an existing group"New value: +"Entities to add to the group's members" - changed
Input schema / properties / all_on / descriptionPrevious value: -"If True, all entities must be on for group to be on"New value: +"If True, all entities must be on for group to be on. Core does not expose the mode, so it cannot be read back and a backup does not restore it." - changed
Input schema / properties / entities / descriptionPrevious value: -"List of entity IDs for the group. When updating, replaces all entities."New value: +"List of entity IDs for the group. When updating, replaces all entities; an empty list empties the group, or creates it without members." - changed
Input schema / properties / name / descriptionPrevious value: -"Friendly display name for the group"New value: +"Friendly display name; a new group without one is named after object_id" - changed
Input schema / properties / object_id / descriptionPrevious value: -"Group identifier without 'group.' prefix (e.g., 'living_room_lights')"New value: +"Group identifier: a lowercase slug without the 'group.' prefix (e.g., 'living_room_lights')" - changed
Input schema / properties / remove_entities / descriptionPrevious value: -"Remove these entities from an existing group"New value: +"Entities to remove from the group's members"
- Changed
ha_config_set_helper2 fields changed- changed
Input schema / properties / helper_id / descriptionPrevious value: -"Bare ID ('my_button') or full entity ID ('input_button.my_button'). Omit to create a new helper."New value: +"The helper's storage id ('my_button'), not the name of its entity, or its full entity_id ('input_button.my_button'). Omit to create a new helper." - changed
Input schema / properties / icon / descriptionPrevious value: -"Material Design Icon (e.g., 'mdi:bell'); '' or ' ' clears it, except a zone's stored icon (the icon in the zone's own config, set at creation or by ha_set_zone; ha_get_zone shows it), which cannot be removed"New value: +"Material Design Icon (e.g., 'mdi:bell'); '' or ' ' clears it, except a zone's stored icon (the icon in the zone's own config, as the Home Assistant UI stores it; ha_get_zone shows it), which cannot be removed. On create, a storage helper's icon goes into its stored config when Home Assistant's schema for the type has an icon field, otherwise (person, tag, a new zone, or without the ha_mcp_tools component) into the entity registry; an update always sets the registry icon, and the stored icon too where the schema has one. Restoring a storage helper's automatic backup also puts back its registry icon, unless the backup predates registry icons being recorded; a flow helper's backup holds no icon, so its restore does not set one"
- Changed
ha_get_hacs_info3 fields changed- changed
Input schema / properties / category / anyOfPrevious value: -[ - { - "enum": [ - "integration", - "lovelace", - "theme", - "appdaemon", - "python_script" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / category / descriptionPrevious value: -"Filter by category (action='search')"New value: +"Filter by category (action='search'). One of the categories the store reports (search's store.categories); 'lovelace' means 'plugin' (dashboard cards)." - added
Input schema / properties / include_releasesAdded value: +{ + "default": false, + "description": "Add the repository's GitHub releases and the release notes of versions newer than the installed one (action='info')", + "type": "boolean" +}
- Changed
ha_get_history4 fields changed- changed
Input schema / properties / end_time / descriptionPrevious value: -"End time: ISO datetime. Default: now"New value: +"End time: same formats as start_time. Omit it to end at the current time" - changed
Input schema / properties / limit / descriptionPrevious 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)."New value: +"Max entries per entity. Default: 100. For source=\"history\": state rows; with summary=True, the states listed in summary.states, longest first. For source=\"statistics\": aggregated rows. With multiple entity_ids, total rows returned can reach limit ร len(entity_ids)." - changed
Input schema / properties / start_time / descriptionPrevious value: -"Start time: ISO datetime or relative (e.g., '24h', '7d', '30d'). Default: 24h ago for history, 30d ago for statistics"New value: +"Start time: ISO datetime or a look-back from now with unit min, h, d, w or m (= 30 days), e.g. '30min' = 30 minutes ago. Default: 24h ago for history, 30d ago for statistics" - added
Input schema / properties / summaryAdded value: +{ + "default": false, + "description": "Return per-entity totals instead of state rows: changes (switches to a different state; the state at the window start is not one, but without that state, e.g. with core_options {'include_start_time_state': false} or purged history, the first recorded state counts as one), first_change and last_change (null without changes), and per state its periods (separate spells) and duration_s (seconds, up to end_time or now, whichever is earlier). distinct_states counts every state, listed or not. Ignored when source=\"statistics\".", + "type": "boolean" +}
- Changed
ha_get_logs13 fields changed- added
Input schema / properties / context_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Logbook only: only events sharing this context - 'what caused this'. Cannot be combined with entity_id or device_id; works on both transports when used alone." +} - added
Input schema / properties / device_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Logbook only: native Core device filter - events attributed to this device and the context chains they caused. State changes of the device's entities are not included; filter by entity_id for those. Requires the WebSocket path." +} - changed
Input schema / properties / entity_id / descriptionPrevious value: -"Logbook only: restrict to this entity."New value: +"Logbook only: restrict to one entity, or a comma-separated list of entity IDs." - changed
Input schema / properties / level / descriptionPrevious value: -"system / error_log only: keep only entries at exactly this level (ERROR, WARNING, INFO, DEBUG, CRITICAL); it is not a threshold."New value: +"system / error_log only: keep only entries at exactly this level (ERROR, WARNING, INFO, DEBUG, CRITICAL); it is not a threshold. For source='error_log' it matches the record's header only, so matching text in a traceback frame does not satisfy it." - changed
Input schema / properties / limit / descriptionPrevious value: -"Max entries/lines to return. Does not apply to source='error_log' with structured=True."New value: +"Max entries/lines to return. For source='error_log' with structured=False it bounds returned lines: whole records are selected within it, and a record longer than it is cut (header and last lines kept) with a warning naming how many lines were cut. Does not apply to source='error_log' with structured=True." - added
Input schema / properties / offset / anyOfAdded value: +[ + { + "type": "integer" + }, + { + "type": "string" + } +] - changed
Input schema / properties / offset / descriptionPrevious 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."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, or is the opaque token a previous page returned in 'next_offset' on Supervisor/journald installs, which keeps paging exact while new log lines arrive (`structured=True` accepts and returns that token too); 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." - removed
Input schema / properties / offset / minimumRemoved value: -0 - removed
Input schema / properties / offset / typeRemoved value: -"integer" - changed
Input schema / properties / order / descriptionPrevious 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."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; error_log orders whole records and keeps each record's lines in place. Ignored for source='logger', and for source='error_log' with structured=True." - changed
Input schema / properties / search / descriptionPrevious 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."New value: +"Keyword filter on entries/lines. For source='system' it matches message, name and exception text; for the raw error_log path it matches any line of a record, so a traceback frame brings back its header and siblings. Structured error_log mode matches message and logger name only; source='logger' matches the integration domain." - changed
Input schema / properties / slug / descriptionPrevious 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."New 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, multicast โ here 'supervisor' is the Supervisor service's own logs, not an app with that name." - changed
Input schema / properties / source / descriptionPrevious 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."New value: +"'logbook': entity state-change history. 'system': HA's structured system_log entries (errors, warnings) with full exception tracebacks; search also matches those exceptions. 'error_log': raw log text (home-assistant.log on container/pip installs; HA Core's journald stream on Supervisor-backed installs), grouped into records: a header line starts one and every following line that is not itself a header joins it โ traceback frames, the blank lines around chained exceptions, and non-HA banners such as s6-overlay restart lines. '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."
- Changed
ha_get_overview1 field changed- changed
Input schema / properties / fields / descriptionPrevious 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."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, mcp_client. 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), ``ha_mcp_update`` (when an update check applies) and ``mcp_client`` are emitted regardless of ``fields=`` projection."
- Changed
ha_get_zone1 field changed- changed
Input schema / properties / zone_id / descriptionPrevious value: -"Zone ID to get details for (from ha_get_zone() list)."New value: +"Zone ID or entity_id to get details for (from the ha_get_zone() list)."
- Changed
ha_manage_app2 fields changed- changed
Input schema / properties / path / descriptionPrevious value: -"Proxy mode: API path relative to the app (add-on) root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode."New value: +"Proxy mode: API path relative to the app (add-on) root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode. ESPHome 2026.10+: POST JSON-RPC to '/api/mcp' (initialize, notifications/initialized, tools/list, then tools/call); check response.error first, then response.result.isError." - changed
Input schema / properties / port / descriptionPrevious 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."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 and ESPHome, reject direct access unless their leave_front_door_open option is enabled and the app is restarted."
- Changed
ha_manage_backup13 fields changed- added
Input schema / properties / include_addonsAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "(snapshot.create) Custom backup: slugs of the installed apps (add-ons) to include. Supervisor installs only." +} - added
Input schema / properties / include_all_addonsAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "(snapshot.create) Custom backup: include every app (add-on); not together with include_addons. Supervisor installs only." +} - added
Input schema / properties / include_databaseAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "(snapshot.create) Custom backup: include the database (history); needs include_homeassistant=True." +} - added
Input schema / properties / include_foldersAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "(snapshot.create) Custom backup: folders to include (`share`, `addons/local`, `ssl`, `media`). Supervisor installs only." +} - added
Input schema / properties / include_homeassistantAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "(snapshot.create) Custom backup: include Home Assistant's configuration." +} - changed
Input schema / properties / name / descriptionPrevious value: -"(snapshot.create) Tarball name. Auto-generated if not provided."New value: +"(snapshot.create) Tarball name. Auto-generated if not provided. Home Assistant names a full backup made with its automatic-backup settings itself." - added
Input schema / properties / restore_addonsAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "(snapshot.restore) Slugs of apps (add-ons) in the backup to restore, from its `addons` in the snapshot list. Default: none. Supervisor installs only." +} - added
Input schema / properties / restore_database / anyOfAdded value: +[ + { + "type": "boolean" + }, + { + "type": "null" + } +] - changed
Input schema / properties / restore_database / defaultPrevious value: -falseNew value: +null - changed
Input schema / properties / restore_database / descriptionPrevious value: -"(snapshot.restore) Include the database in the restore; otherwise config only."New value: +"(snapshot.restore) Include the database in the restore. Default: restore it when the backup contains it. Must be false for a backup without one; on Supervisor installs it must match the backup." - removed
Input schema / properties / restore_database / typeRemoved value: -"boolean" - added
Input schema / properties / restore_foldersAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "(snapshot.restore) Folders in the backup to restore, from its `folders` in the snapshot list (`share`, `addons/local`, `ssl`, `media`). Default: none. Supervisor installs only." +} - added
Input schema / properties / safety_backupAdded value: +{ + "default": false, + "description": "(snapshot.restore) Back up what the restore replaces (configuration, plus the database, apps and folders it restores) first, as a manual backup on the default location. Off by default; offer it to the user, who decides. Needs the default backup password.", + "type": "boolean" +}
- Changed
ha_manage_hacs3 fields changed- changed
Input schema / properties / category / anyOfPrevious value: -[ - { - "enum": [ - "integration", - "lovelace", - "theme", - "appdaemon", - "python_script" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / category / descriptionPrevious value: -"Repository category (action='add_repository')"New value: +"Repository category (action='add_repository'). One of the categories the store reports (search's store.categories); 'lovelace' means 'plugin' (dashboard cards)." - added
Input schema / properties / confirm_replace_built_inAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Marketplace only (action='download'): install a repository that replaces the built-in integration of the same domain. Set it only after the user agrees." +}
- Changed
ha_remove_helpers_integrations1 field changed- changed
Input schema / properties / target / descriptionPrevious 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, with helper_type omitted, e.g. value from ha_get_integration(); (d) parent config entry_id for a config subentry."New value: +"What to remove. One of: (a) bare helper_id for SIMPLE helpers: the helper's storage id, not the name of its entity, 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, with helper_type omitted, e.g. value from ha_get_integration(); (d) parent config entry_id for a config subentry."
- Changed
ha_remove_zone2 fields changed- added
Input schema / properties / waitAdded value: +{ + "default": true, + "description": "Wait for the zone to be gone before returning. Set to False for bulk operations.", + "type": "boolean" +} - changed
Input schema / properties / zone_id / descriptionPrevious value: -"Zone ID to remove"New value: +"Zone ID or entity_id of the zone to remove"
- Changed
ha_restart1 field changed- added
Input schema / properties / safe_modeAdded value: +{ + "default": false, + "description": "Restart in safe mode: no custom integrations or custom frontend resources load until the next normal restart.", + "type": "boolean" +}
- Changed
ha_set_zone5 fields changed- changed
Input schema / properties / icon / descriptionPrevious value: -"Material Design Icon (e.g., 'mdi:briefcase', 'mdi:school')"New value: +"Material Design Icon (e.g., 'mdi:briefcase'). On update, '' clears it, except an icon stored in the zone itself (as the Home Assistant UI stores it; ha_get_zone shows it), which cannot be removed" - changed
Input schema / properties / passive / descriptionPrevious value: -"Passive mode - if True, zone will not trigger enter/exit automations (defaults to False on create)"New value: +"Passive mode: the zone is hidden in the frontend and not used for device tracker state, but automations can still use it (false when omitted on create)" - changed
Input schema / properties / radius / descriptionPrevious value: -"Radius of the zone in meters (must be > 0, defaults to 100 on create)"New value: +"Radius of the zone in meters; Home Assistant uses 100 when it is omitted on create" - added
Input schema / properties / waitAdded value: +{ + "default": true, + "description": "Wait for the zone to be queryable before returning. Set to False for bulk operations.", + "type": "boolean" +} - changed
Input schema / properties / zone_id / descriptionPrevious value: -"Zone ID to update (use ha_get_zone to find IDs)"New value: +"Zone ID or entity_id of the zone to update (from ha_get_zone)"
15 tool updates
v8.7.0- Changed
ha_config_get_dashboard12 fields changed- changed
Input schema / properties / card_type / descriptionPrevious value: -"Find cards by type, e.g. 'tile', 'button', 'heading'."New value: +"Search: cards of this type, e.g. 'tile'; 'badge' selects view badges instead (alone, it lists them all). With describe=True: the type to describe." - added
Input schema / properties / describeAdded value: +{ + "default": false, + "description": "Return card_type's fields from HA's card editor", + "type": "boolean" +} - changed
Input schema / properties / entity_id / descriptionPrevious 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."New value: +"Search: cards whose entity/entities field holds this entity ID; '*' matches any run of characters (including none) and the whole ID must match (e.g. 'sensor.temperature_*')." - changed
Input schema / properties / force_reload / descriptionPrevious value: -"Force reload from storage (bypass cache). Not applicable in search mode."New value: +"Get only: bypass the config cache (searches always read fresh config)." - changed
Input schema / properties / heading / descriptionPrevious value: -"Find cards by heading/title text (case-insensitive partial match)."New value: +"Search: cards whose heading/title contains this text (case-insensitive)." - changed
Input schema / properties / include_config / descriptionPrevious 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."New value: +"Search: include each matched card's own config. A container's body includes its nested cards, which are also matches, so nested stacks multiply the payload. Withheld, with a warning, for a dashboard not provably storage-mode (a YAML config may carry resolved !secret values)." - changed
Input schema / properties / include_screenshot / descriptionPrevious 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."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." - changed
Input schema / properties / list_only / descriptionPrevious value: -"When True, url_path is ignored."New value: +"List every dashboard's metadata instead of reading one; ignored when a search parameter is given." - removed
Input schema / properties / modeRemoved value: -{ - "anyOf": [ - { - "const": "search", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Set to 'search' (requires query)." -} - changed
Input schema / properties / query / descriptionPrevious value: -"With mode='search': the entity_id or substring to find across all storage-mode dashboards. Ignored otherwise."New value: +"Search: text or entity ID contained in any value a card holds (case-insensitive); the matching values are listed under `matched`." - changed
Input schema / properties / url_path / descriptionPrevious value: -"Dashboard URL path (e.g., 'lovelace-home'). Use 'default' for default dashboard."New value: +"Dashboard URL path (e.g. 'lovelace-home'); 'default' for the default dashboard." - changed
Input schema / properties / view_path / descriptionPrevious 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."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."
- Changed
ha_config_list_helpers5 fields changed- added
Input schema / properties / describeAdded value: +{ + "default": false, + "description": "Instead of listing, return the fields ha_config_set_helper accepts in config for helper_type, as Home Assistant reports them (the form the HA UI shows).", + "type": "boolean" +} - added
Input schema / properties / helper_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "describe only: an existing helper (config entry id for flow helpers, entity_id or id otherwise); each field then carries its current value." +} - changed
Input schema / properties / helper_type / anyOfPrevious 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" - } -]New value: +[ + { + "enum": [ + "input_button", + "input_boolean", + "input_select", + "input_number", + "input_text", + "input_datetime", + "counter", + "timer", + "schedule", + "zone", + "person", + "tag" + ], + "type": "string" + }, + { + "const": "all", + "type": "string" + }, + { + "type": "string" + } +] - changed
Input schema / properties / helper_type / descriptionPrevious value: -"Helper 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)."New value: +"Helper type to list: a storage type (input_boolean, counter, timer, ...), a helper flow Home Assistant lists (template, group, utility_meter, ...), or 'all' for every type in one call." - added
Input schema / properties / menu_choiceAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "describe only: sub-type of a menu-based helper (template, group), e.g. 'sensor'." +}
- Changed
ha_config_set_helper35 fields changed- changed
Input schema / properties / area_id / descriptionPrevious value: -"Area/room ID to assign the helper to"New value: +"Area ID for the helper; '' or ' ' clears it" - changed
Input schema / properties / category / descriptionPrevious value: -"Category 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."New value: +"Category ID for this helper (ha_config_get_category(scope='helpers') lists them, ha_config_set_category() creates one); '' or ' ' clears it." - changed
Input schema / properties / config / descriptionPrevious 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."New value: +"Type-specific fields. On update it is a patch: a field you omit keeps its current value. SIMPLE types take these keys: input_select: options (list, required), initial. input_number: min, max (required), step, unit_of_measurement, mode ('box'/'slider'), initial. input_text: min, max (length), mode ('text'/'password'), initial, unit_of_measurement, pattern (regex the value must match). input_datetime: has_date, has_time, initial. input_boolean: initial. counter: initial (starting value), minimum, maximum, step, restore (default true). timer: duration ('HH:MM:SS' or seconds), restore (default false). schedule: monday..sunday, each a list of {'from': 'HH:MM', 'to': 'HH:MM'} with optional 'data' dict of extra attributes. zone: latitude, longitude (both required), radius (meters, default 100), passive (won't trigger person state changes). person: user_id, device_trackers (device_tracker entity IDs), picture (URL). tag: tag_id (omit on create to auto-generate), description. On input_* types, `initial` โ even false/0 โ disables last-state restore and forces that value on every HA restart. ha_config_list_helpers(helper_type, describe=True) lists a type's fields. FLOW types and config_subentry take the flow's fields; 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." - removed
Input schema / properties / descriptionRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Description for tag" -} - removed
Input schema / properties / device_trackersRemoved value: -{ - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "List of device_tracker entity IDs for person" -} - removed
Input schema / properties / durationRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Default duration for timer in format 'HH:MM:SS' or seconds (e.g., '0:05:00' for 5 minutes)" -} - removed
Input schema / properties / fridayRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": true, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Schedule time ranges for Friday; same shape as monday." -} - removed
Input schema / properties / has_dateRemoved value: -{ - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Include date component for input_datetime" -} - removed
Input schema / properties / has_timeRemoved value: -{ - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Include time component for input_datetime" -} - added
Input schema / properties / helper_type / anyOfAdded value: +[ + { + "enum": [ + "input_button", + "input_boolean", + "input_select", + "input_number", + "input_text", + "input_datetime", + "counter", + "timer", + "schedule", + "zone", + "person", + "tag" + ], + "type": "string" + }, + { + "const": "config_subentry", + "type": "string" + }, + { + "type": "string" + } +] - changed
Input schema / properties / helper_type / descriptionPrevious value: -"Type of helper entity to create or update"New value: +"Type of helper to create or update." - removed
Input schema / properties / helper_type / enumRemoved 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" -] - removed
Input schema / properties / helper_type / typeRemoved value: -"string" - changed
Input schema / properties / icon / descriptionPrevious value: -"Material Design Icon (e.g., 'mdi:bell', 'mdi:toggle-switch')"New value: +"Material Design Icon (e.g., 'mdi:bell'); '' or ' ' clears it, except a zone's stored icon (the icon in the zone's own config, set at creation or by ha_set_zone; ha_get_zone shows it), which cannot be removed" - removed
Input schema / properties / initialRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Initial 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)." -} - removed
Input schema / properties / latitudeRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Latitude for zone (required for zone)" -} - removed
Input schema / properties / longitudeRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Longitude for zone (required for zone)" -} - removed
Input schema / properties / max_valueRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Maximum value (input_number/counter) or maximum length (input_text). Also accepts shorthand 'max'." -} - removed
Input schema / properties / min_valueRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Minimum value (input_number/counter) or minimum length (input_text). Also accepts shorthand 'min'." -} - removed
Input schema / properties / modeRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Display mode: 'box'/'slider' for input_number, 'text'/'password' for input_text" -} - removed
Input schema / properties / mondayRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": true, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Schedule 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'}})" -} - removed
Input schema / properties / optionsRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "List of options for input_select (required for input_select)" -} - removed
Input schema / properties / passiveRemoved value: -{ - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Passive zone (won't trigger state changes for person entities)" -} - removed
Input schema / properties / pictureRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Picture URL for person entity" -} - removed
Input schema / properties / radiusRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Radius in meters for zone (default: 100)" -} - removed
Input schema / properties / restoreRemoved value: -{ - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Restore state after restart (counter, timer). Defaults to True for counter, False for timer" -} - removed
Input schema / properties / saturdayRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": true, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Schedule time ranges for Saturday; same shape as monday." -} - removed
Input schema / properties / stepRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Step/increment value for input_number or counter" -} - removed
Input schema / properties / sundayRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": true, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Schedule time ranges for Sunday; same shape as monday." -} - removed
Input schema / properties / tag_idRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Tag ID. On create, omit to auto-generate a uuid4 hex. On update, the existing tag_id is required (passed via helper_id)." -} - removed
Input schema / properties / thursdayRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": true, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Schedule time ranges for Thursday; same shape as monday." -} - removed
Input schema / properties / tuesdayRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": true, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Schedule time ranges for Tuesday; same shape as monday." -} - removed
Input schema / properties / unit_of_measurementRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Unit of measurement for input_number (e.g., 'ยฐC', '%', 'W'). Also accepts shorthand 'unit'." -} - removed
Input schema / properties / user_idRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "User ID to link to person entity" -} - removed
Input schema / properties / wednesdayRemoved value: -{ - "anyOf": [ - { - "items": { - "additionalProperties": true, - "type": "object" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Schedule time ranges for Wednesday; same shape as monday." -}
- Changed
ha_get_camera_image2 fields changed- changed
Input schema / properties / height / descriptionPrevious value: -"Height to resize the image to"New value: +"Height for the resized image. Home Assistant rescales only when both width and height are given and the camera serves JPEG." - changed
Input schema / properties / width / descriptionPrevious value: -"Width to resize the image to"New value: +"Width for the resized image. Home Assistant rescales only when both width and height are given and the camera serves JPEG."
- Changed
ha_get_history4 fields changed- added
Input schema / properties / core_optionsAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Additional native Core request fields, e.g. {'units': {'energy': 'MWh'}} for statistics. Core validates them. Cannot override fields controlled by the tool's parameters or query limits." +} - changed
Input schema / properties / fields / descriptionPrevious 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."New value: +"Return only the specified keys within data 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 and timezone metadata remain outside data and are always retained." - added
Input schema / properties / include_schemaAdded value: +{ + "default": false, + "description": "Include the running Core's native request schema when the component supports discovery.", + "type": "boolean" +} - changed
Input schema / properties / statistic_types / descriptionPrevious value: -"Statistics types: \"mean\", \"min\", \"max\", \"sum\", \"state\", \"change\". Default: all. Ignored when source=\"history\""New value: +"Native Core statistics types (for example \"sum\", \"change\", \"last_reset\"). Default: Core chooses all. Use include_schema=True to discover the installed contract. Ignored when source=\"history\""
- Changed
ha_get_logs1 field changed- changed
Input schema / properties / compact / descriptionPrevious value: -"Logbook only: strip attribute dicts to save context."New value: +"logbook / system only: save context. Logbook strips attribute dicts; system keeps the start and end of each message string within 2000 characters (counted in 'truncated_messages'). search still matches the full message; text inside the cut is not shown. To read one message whole, pass False with a narrow search and limit=1."
- Changed
ha_get_operation_status1 field changed- changed
Input schema / properties / timeout_seconds / defaultPrevious value: -10New value: +8
- Changed
ha_get_skill_guide1 field changed- changed
Input schema / properties / file / descriptionPrevious value: -"Path of the file to read, exactly as SKILL.md links it (e.g. 'references/automation-patterns.md'). Omit to read SKILL.md."New value: +"Path of the file to read, exactly as SKILL.md links it (e.g. 'references/triggers-and-conditions.md'). Omit to read SKILL.md."
- Changed
ha_manage_energy_prefs5 fields changed- changed
Input schema / properties / config_hash / anyOfPrevious value: -[ - { - "type": "string" - }, - { - "additionalProperties": { - "type": "string" - }, - "propertyNames": { - "enum": [ - "energy_sources", - "device_consumption", - "device_consumption_water" - ] - }, - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "type": "string" + }, + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } +] - changed
Input schema / properties / dry_run / descriptionPrevious 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."New value: +"If True, preview without saving. With the component, validates the complete proposal through Core's registered save schema. Without that capability, explicitly reports proposal validation unavailable. Semantic energy/validate checks persisted state only." - added
Input schema / properties / include_schemaAdded value: +{ + "default": false, + "description": "With mode='get', describe the running Core's save schema. Unavailable without the component.", + "type": "boolean" +} - added
Input schema / properties / include_statisticsAdded value: +{ + "default": false, + "description": "With mode='get', include native recorder metadata and resolved output units for all configured statistic references. Ignored for other modes.", + "type": "boolean" +} - changed
Input schema / properties / source / descriptionPrevious value: -"Single 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."New value: +"Single native energy_sources entry for mode='add_source'. Use mode='get', include_schema=True to inspect the running Core's fields and requirements; dry_run=True validates without saving."
- Changed
ha_manage_radio1 field changed- changed
Input schema / properties / params / descriptionPrevious value: -"Action-specific parameters (e.g. code, pin, channel, property, value)."New value: +"Action-specific parameters (e.g. code, pin, channel, property, value). Z-Wave get_config_param: property (parameter number), endpoint (default 0), property_key (partial-parameter bit mask), refresh (default False; full root parameters only)."
- Changed
ha_remove_helpers_integrations4 fields changed- changed
Input schema / properties / helper_type / anyOfPrevious 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" - } -]New value: +[ + { + "enum": [ + "input_button", + "input_boolean", + "input_select", + "input_number", + "input_text", + "input_datetime", + "counter", + "timer", + "schedule", + "zone", + "person", + "tag" + ], + "type": "string" + }, + { + "const": "config_subentry", + "type": "string" + }, + { + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / helper_type / descriptionPrevious value: -"Helper 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."New value: +"Helper type of target; ROUTING says which value takes which removal path." - changed
Input schema / properties / target / descriptionPrevious 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."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, with helper_type omitted, e.g. value from ha_get_integration(); (d) parent config entry_id for a config subentry." - changed
Input schema / properties / wait / descriptionPrevious value: -"Wait for entity removal. Default: True. Ignored when helper_type=None or helper_type='config_subentry' (no entity poll, require_restart returned)."New value: +"Wait for entity removal. Default: True. Ignored for a direct config entry delete (an entry_id target) or helper_type='config_subentry' (no entity poll, require_restart returned)."
- Changed
ha_report_issue4 fields changed- changed
Input schema / properties / description / descriptionPrevious value: -"What went wrong in markdown: steps to reproduce, expected and actual behavior. For agent_behavior: what you did and what you should have done."New value: +"What went wrong in markdown: steps to reproduce, expected and actual behavior. For agent_behavior: what you did and what you should have done. For feature_request: what ha-mcp should do, why the user needs it, and what you tried." - changed
Input schema / properties / report_type / descriptionPrevious value: -"'runtime_bug' when ha-mcp errored or behaved wrongly; 'agent_behavior' when the user says you used the wrong tool or worked inefficiently."New value: +"'runtime_bug' when ha-mcp errored or behaved wrongly; 'agent_behavior' when the user says you used the wrong tool or worked inefficiently; 'feature_request' when the user wants ha-mcp to do something it cannot do yet." - changed
Input schema / properties / report_type / enumPrevious value: -[ - "runtime_bug", - "agent_behavior" -]New value: +[ + "runtime_bug", + "agent_behavior", + "feature_request" +] - changed
Input schema / properties / title / descriptionPrevious value: -"One-line summary of what broke, for the issue title."New value: +"One-line summary of what broke or what is requested, for the issue title."
- Changed
ha_set_area_or_floor8 fields changed- removed
Input schema / properties / floor_id / defaultRemoved value: -null - changed
Input schema / properties / floor_id / descriptionPrevious value: -"Floor assignment when kind='area' (use empty string to clear)."New value: +"Floor assignment when kind='area' (null or empty string to clear; omit to leave unchanged)." - added
Input schema / properties / humidity_entity_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Sensor the area reports humidity from, when kind='area' (a sensor.* entity with device_class humidity; null or empty string to clear). Omit to leave unchanged." +} - removed
Input schema / properties / icon / defaultRemoved value: -null - changed
Input schema / properties / icon / descriptionPrevious value: -"Material Design Icon (e.g., 'mdi:sofa', 'mdi:home-floor-1', empty string to remove)"New value: +"Material Design Icon (e.g., 'mdi:sofa', 'mdi:home-floor-1'; null or empty string to remove; omit to leave unchanged)" - removed
Input schema / properties / picture / defaultRemoved value: -null - changed
Input schema / properties / picture / descriptionPrevious value: -"Picture URL when kind='area' (empty string to remove)."New value: +"Picture URL when kind='area' (null or empty string to remove; omit to leave unchanged)." - added
Input schema / properties / temperature_entity_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Sensor the area reports temperature from, when kind='area' (a sensor.* entity with device_class temperature; null or empty string to clear). Omit to leave unchanged." +}
- Changed
ha_set_device3 fields changed- changed
Input schema / properties / area_id / descriptionPrevious value: -"Area/room ID to assign the device to. Use empty string '' to unassign."New value: +"Area/room ID to assign the device to. Use empty string '' to unassign. A whitespace-only value acts like '', for clients that cannot send an empty string." - changed
Input schema / properties / disabled_by / descriptionPrevious value: -"Set to 'user' to disable the device, or '' (empty string) to enable it. Omit to leave the disabled state unchanged."New value: +"Set to 'user' to disable the device, or '' (empty string) to enable it. Omit to leave the disabled state unchanged. A whitespace-only value acts like '', for clients that cannot send an empty string." - changed
Input schema / properties / name / descriptionPrevious value: -"New display name for the device (sets name_by_user)"New value: +"New display name for the device (sets name_by_user). '' clears name_by_user and reverts to the integration-provided name. A whitespace-only value acts like '', for clients that cannot send an empty string."
- Changed
ha_set_entity6 fields changed- changed
Input schema / properties / area_id / descriptionPrevious value: -"Area/room ID to assign the entity to. Use empty string '' to unassign from current area."New value: +"Area/room ID to assign the entity to. Use empty string '' to unassign from current area. A whitespace-only value acts like '', for clients that cannot send an empty string." - changed
Input schema / properties / device_class / descriptionPrevious 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."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. A whitespace-only value acts like '', for clients that cannot send an empty string." - changed
Input schema / properties / icon / descriptionPrevious value: -"Icon for the entity (e.g., 'mdi:thermometer'). Use empty string '' to remove custom icon."New value: +"Icon for the entity (e.g., 'mdi:thermometer'). Use empty string '' to remove custom icon. A whitespace-only value acts like '', for clients that cannot send an empty string." - changed
Input schema / properties / name / descriptionPrevious value: -"Display name for the entity. Use empty string '' to remove custom name and revert to default."New value: +"Display name for the entity. Use empty string '' to remove custom name and revert to default. A whitespace-only value acts like '', for clients that cannot send an empty string." - changed
Input schema / properties / new_device_name / descriptionPrevious value: -"New display name for the associated device. If provided, both entity and device are updated in one operation."New value: +"New display name for the associated device. If provided, both entity and device are updated in one operation. Empty or whitespace-only never changes the device name: it is reported under warnings when other fields are given and is an error when it is the only update. To clear the device name use ha_set_device(name='')." - added
Input schema / properties / use_device_nameAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "description": "HA 2026.10's 'Use device name' switch. True names the entity after its device and follows device renames; False restores the integration's default name. Omit to leave it as is; not combinable with name." +}
69 tool updates
v8.6.0- Changed
ha_bulk_control5 fields changed- added
Input schema / properties / dry_run / descriptionAdded value: +"Selector mode only; True is rejected in operations mode." - changed
Input schema / properties / operations / descriptionPrevious 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'." - added
Input schema / properties / parallel / descriptionAdded value: +"Dispatch operations concurrently (default) or one at a time when False." - added
Input schema / properties / timeout_seconds / descriptionAdded 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." - added
Input schema / properties / validate_first / descriptionAdded 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."
- Changed
ha_call_event2 fields changed- added
Input schema / properties / data / descriptionAdded value: +"Optional event payload, delivered to subscribers as the event's data (trigger.event.data in an automation)." - added
Input schema / properties / event_type / descriptionAdded value: +"Event type to fire, e.g. 'my_custom_event'."
- Changed
ha_call_service8 fields changed- changed
Input schema / properties / domain / descriptionPrevious 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." - changed
Input schema / properties / entity_id / descriptionPrevious 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." - changed
Input schema / properties / result_attribute_keys / descriptionPrevious 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)." - changed
Input schema / properties / result_fields / descriptionPrevious 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." - changed
Input schema / properties / return_response / descriptionPrevious 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'." - changed
Input schema / properties / service / descriptionPrevious 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." - changed
Input schema / properties / verbose / descriptionPrevious 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." - changed
Input schema / properties / ws_command / descriptionPrevious 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."
- Changed
ha_config_delete_dashboard_resource1 field changed- changed
Input schema / properties / resource_id / descriptionPrevious value: -"Resource ID to delete. Get from ha_config_list_dashboard_resources()"New value: +"Resource ID to delete."
- Changed
ha_config_get_calendar_events1 field changed- changed
Input schema / properties / end / descriptionPrevious 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)"
- Changed
ha_config_get_category1 field changed- changed
Input schema / properties / category_id / descriptionPrevious value: -"ID of the category to retrieve. If omitted, lists all categories for the scope."New value: +"ID of the category to retrieve."
- Changed
ha_config_get_dashboard10 fields changed- changed
Input schema / properties / card_type / descriptionPrevious 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'." - changed
Input schema / properties / entity_id / descriptionPrevious 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." - changed
Input schema / properties / force_reload / descriptionPrevious 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." - changed
Input schema / properties / heading / descriptionPrevious 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)." - changed
Input schema / properties / include_config / descriptionPrevious 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." - changed
Input schema / properties / include_screenshot / descriptionPrevious 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." - changed
Input schema / properties / list_only / descriptionPrevious value: -"If True, list all dashboards instead of getting config. When True, url_path is ignored."New value: +"When True, url_path is ignored." - changed
Input schema / properties / mode / descriptionPrevious 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)." - changed
Input schema / properties / url_path / descriptionPrevious 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." - changed
Input schema / properties / view_path / descriptionPrevious 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."
- Changed
ha_config_get_label1 field changed- changed
Input schema / properties / label_id / descriptionPrevious value: -"ID of the label to retrieve. If omitted, lists all labels."New value: +"ID of the label to retrieve."
- Changed
ha_config_get_scene9 fields changed- added
Input schema / properties / limitAdded value: +{ + "default": 20, + "description": "Maximum scenes per page", + "maximum": 100, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / offsetAdded value: +{ + "default": 0, + "description": "Pagination offset", + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / queryAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Filter scene names or IDs" +} - added
Input schema / properties / scene_id / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / scene_id / defaultAdded value: +null - changed
Input schema / properties / scene_id / descriptionPrevious 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." - removed
Input schema / properties / scene_id / typeRemoved value: -"string" - added
Input schema / properties / search_in_configAdded value: +{ + "default": false, + "description": "Also search full stored scene attribute values within a bounded scan", + "type": "boolean" +} - removed
Input schema / requiredRemoved value: -[ - "scene_id" -]
- Changed
ha_config_list_dashboard_resources3 fields changed- changed
Input schema / properties / include_content / descriptionPrevious 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." - changed
Input schema / properties / limit / descriptionPrevious value: -"Max resources to return per page (default: 100)"New value: +"Max resources to return per page" - changed
Input schema / properties / offset / descriptionPrevious value: -"Number of resources to skip for pagination (default: 0)"New value: +"Number of resources to skip for pagination"
- Changed
ha_config_list_groups2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Max groups to return per page (default: 100)"New value: +"Max groups to return per page" - changed
Input schema / properties / offset / descriptionPrevious value: -"Number of groups to skip for pagination (default: 0)"New value: +"Number of groups to skip for pagination"
- Changed
ha_config_list_helpers2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Max helpers to return per page (default: 100)"New value: +"Max helpers to return per page" - changed
Input schema / properties / offset / descriptionPrevious value: -"Number of helpers to skip for pagination (default: 0)"New value: +"Number of helpers to skip for pagination"
- Changed
ha_config_remove_automation1 field changed- changed
Input schema / properties / wait / descriptionPrevious value: -"Wait for automation to be fully removed before returning. Default: True."New value: +"Wait for automation to be fully removed before returning."
- Changed
ha_config_remove_calendar_event3 fields changed- changed
Input schema / properties / recurrence_id / descriptionPrevious value: -"Optional recurrence ID for recurring events"New value: +"Recurrence ID for recurring events" - changed
Input schema / properties / recurrence_range / anyOfPrevious value: -[ - { - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "const": "THISANDFUTURE", + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / recurrence_range / descriptionPrevious 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."
- Changed
ha_config_remove_group1 field changed- changed
Input schema / properties / wait / descriptionPrevious value: -"Wait for group to be fully removed before returning. Default: True."New value: +"Wait for group to be fully removed before returning."
- Changed
ha_config_remove_scene1 field changed- changed
Input schema / properties / wait / descriptionPrevious value: -"Wait for scene to be fully removed before returning. Default: True."New value: +"Wait for scene to be fully removed before returning."
- Changed
ha_config_remove_script1 field changed- changed
Input schema / properties / wait / descriptionPrevious value: -"Wait for script to be fully removed before returning. Default: True."New value: +"Wait for script to be fully removed before returning."
- Changed
ha_config_set_automation8 fields changed- changed
Input schema / properties / config / descriptionPrevious 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." - changed
Input schema / properties / config_hash / descriptionPrevious 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)." - added
Input schema / properties / enabledAdded 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." +} - changed
Input schema / properties / identifier / descriptionPrevious 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." - changed
Input schema / properties / python_transform / descriptionPrevious 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" - added
Input schema / properties / run_actionsAdded 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" +} - changed
Input schema / properties / take_control_of_blueprint / descriptionPrevious 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." - changed
Input schema / properties / wait / descriptionPrevious 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."
- Changed
ha_config_set_calendar_event6 fields changed- changed
Input schema / properties / description / descriptionPrevious value: -"Optional event description"New value: +"Event description" - changed
Input schema / properties / location / descriptionPrevious value: -"Optional event location"New value: +"Event location" - added
Input schema / properties / recurrence_idAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Only meaningful with 'uid': identifies one occurrence of a recurring series to update." +} - added
Input schema / properties / recurrence_rangeAdded 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." +} - changed
Input schema / properties / rrule / descriptionPrevious 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." - added
Input schema / properties / uidAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "UID of an existing event to update. Omit to create a new event." +}
- Changed
ha_config_set_category1 field changed- changed
Input schema / properties / category_id / descriptionPrevious value: -"Category ID for updates. If not provided, creates a new category."New value: +"Category ID for updates."
- Changed
ha_config_set_dashboard3 fields changed- changed
Input schema / properties / patch / descriptionPrevious 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." - changed
Input schema / properties / python_transform / descriptionPrevious 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" - changed
Input schema / properties / return_screenshot / descriptionPrevious 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."
- Changed
ha_config_set_dashboard_resource3 fields changed- changed
Input schema / properties / content / descriptionPrevious 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." - changed
Input schema / properties / resource_type / descriptionPrevious 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)" - changed
Input schema / properties / url / descriptionPrevious 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."
- Changed
ha_config_set_group5 fields changed- changed
Input schema / properties / add_entities / descriptionPrevious value: -"Add these entities to an existing group (mutually exclusive with entities)"New value: +"Add these entities to an existing group" - changed
Input schema / properties / all_on / descriptionPrevious 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" - changed
Input schema / properties / entities / descriptionPrevious 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." - changed
Input schema / properties / remove_entities / descriptionPrevious value: -"Remove these entities from an existing group (mutually exclusive with entities)"New value: +"Remove these entities from an existing group" - changed
Input schema / properties / wait / descriptionPrevious 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."
- Changed
ha_config_set_helper13 fields changed- changed
Input schema / properties / action / descriptionPrevious 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." - changed
Input schema / properties / config / descriptionPrevious 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." - changed
Input schema / properties / friday / descriptionPrevious 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." - changed
Input schema / properties / helper_id / descriptionPrevious 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." - changed
Input schema / properties / name / descriptionPrevious 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=...)." - changed
Input schema / properties / saturday / descriptionPrevious 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." - changed
Input schema / properties / subentry_id / descriptionPrevious 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'." - changed
Input schema / properties / sunday / descriptionPrevious 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." - changed
Input schema / properties / tag_id / descriptionPrevious 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)." - changed
Input schema / properties / thursday / descriptionPrevious 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." - changed
Input schema / properties / tuesday / descriptionPrevious 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." - changed
Input schema / properties / wait / descriptionPrevious 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." - changed
Input schema / properties / wednesday / descriptionPrevious 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."
- Changed
ha_config_set_label2 fields changed- added
Input schema / properties / areasAdded 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=[])." +} - changed
Input schema / properties / label_id / descriptionPrevious value: -"Label ID for updates. If not provided, creates a new label."New value: +"Label ID for updates."
- Changed
ha_config_set_scene3 fields changed- added
Input schema / properties / activateAdded 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" +} - changed
Input schema / properties / python_transform / descriptionPrevious 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" - changed
Input schema / properties / wait / descriptionPrevious 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."
- Changed
ha_config_set_script7 fields changed- changed
Input schema / properties / config / descriptionPrevious 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." - changed
Input schema / properties / config_hash / descriptionPrevious 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)." - changed
Input schema / properties / python_transform / descriptionPrevious 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" - added
Input schema / properties / runAdded 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." +} - changed
Input schema / properties / take_control_of_blueprint / descriptionPrevious 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." - added
Input schema / properties / variablesAdded 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." +} - changed
Input schema / properties / wait / descriptionPrevious 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."
- Changed
ha_eval_template12 fields changed- added
Input schema / properties / conditionAdded 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': [...]}." +} - added
Input schema / properties / report_errors / descriptionAdded value: +"Template only: have Home Assistant report render errors and warnings. With false, a failed render is only written to Home Assistant's log." - added
Input schema / properties / strictAdded 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" +} - added
Input schema / properties / template / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / template / defaultAdded value: +null - added
Input schema / properties / template / descriptionAdded value: +"Jinja2 template to render. Omit when using 'condition'." - removed
Input schema / properties / template / typeRemoved value: -"string" - added
Input schema / properties / timeout / descriptionAdded value: +"Template only: maximum render time in seconds" - added
Input schema / properties / timeout / maximumAdded value: +60 - added
Input schema / properties / timeout / minimumAdded value: +1 - added
Input schema / properties / variablesAdded 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'}}}" +} - removed
Input schema / requiredRemoved value: -[ - "template" -]
- Changed
ha_get_app2 fields changed- changed
Input schema / properties / slug / descriptionPrevious 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." - changed
Input schema / properties / source / descriptionPrevious 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."
- Changed
ha_get_automation_traces4 fields changed- changed
Input schema / properties / deduplicate / descriptionPrevious 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." - changed
Input schema / properties / detailed / descriptionPrevious 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." - changed
Input schema / properties / order / descriptionPrevious 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." - changed
Input schema / properties / run_id / descriptionPrevious value: -"Specific trace run_id to retrieve detailed trace. Omit to list recent traces."New value: +"Specific trace run_id to retrieve detailed trace."
- Changed
ha_get_camera_image3 fields changed- added
Input schema / properties / entity_id / descriptionAdded value: +"Camera entity ID (e.g., 'camera.front_door')" - added
Input schema / properties / height / descriptionAdded value: +"Height to resize the image to" - added
Input schema / properties / width / descriptionAdded value: +"Width to resize the image to"
- Changed
ha_get_device4 fields changed- changed
Input schema / properties / detail_level / descriptionPrevious 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." - changed
Input schema / properties / device_id / descriptionPrevious value: -"Device ID to retrieve details for. If omitted, lists devices."New value: +"Device ID to retrieve details for." - changed
Input schema / properties / limit / descriptionPrevious value: -"Max devices to return per page in list mode (default: 50)"New value: +"Max devices to return per page in list mode" - changed
Input schema / properties / offset / descriptionPrevious value: -"Number of devices to skip for pagination (default: 0)"New value: +"Number of devices to skip for pagination"
- Changed
ha_get_entity2 fields changed- changed
Input schema / properties / entity_id / descriptionPrevious 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'])." - changed
Input schema / properties / unique_id / descriptionPrevious 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."
- Changed
ha_get_entity_exposure2 fields changed- changed
Input schema / properties / assistant / descriptionPrevious 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'." - changed
Input schema / properties / entity_id / descriptionPrevious 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."
- Changed
ha_get_hacs_info3 fields changed- changed
Input schema / properties / installed_only / descriptionPrevious value: -"Only return installed repositories (action='search', default: False)"New value: +"Only return installed repositories (action='search')" - changed
Input schema / properties / max_results / descriptionPrevious value: -"Maximum number of results (action='search', default: 10, max: 100)"New value: +"Maximum number of results (action='search')" - changed
Input schema / properties / offset / descriptionPrevious value: -"Results to skip for pagination (action='search', default: 0)"New value: +"Results to skip for pagination (action='search')"
- Changed
ha_get_history8 fields changed- changed
Input schema / properties / fields / descriptionPrevious 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." - changed
Input schema / properties / limit / descriptionPrevious 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)." - changed
Input schema / properties / minimal_response / descriptionPrevious 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\"" - changed
Input schema / properties / offset / descriptionPrevious 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." - changed
Input schema / properties / order / descriptionPrevious 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\"." - changed
Input schema / properties / period / descriptionPrevious 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\"" - changed
Input schema / properties / significant_changes_only / descriptionPrevious 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\"" - changed
Input schema / properties / source / descriptionPrevious 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)."
- Changed
ha_get_integration15 fields changed- changed
Input schema / properties / device_id / descriptionPrevious 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." - changed
Input schema / properties / diagnostics_data_limit / descriptionPrevious 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." - changed
Input schema / properties / diagnostics_data_offset / descriptionPrevious 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." - changed
Input schema / properties / diagnostics_data_path / descriptionPrevious 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." - changed
Input schema / properties / diagnostics_fields / descriptionPrevious 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." - changed
Input schema / properties / diagnostics_truncate_at_bytes / descriptionPrevious 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." - changed
Input schema / properties / entry_id / descriptionPrevious value: -"Config entry ID to get details for. If omitted, lists all integrations."New value: +"Config entry ID to get details for." - changed
Input schema / properties / exact_match / descriptionPrevious 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." - changed
Input schema / properties / include_diagnostics / descriptionPrevious 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." - changed
Input schema / properties / include_knx_project / descriptionPrevious 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." - changed
Input schema / properties / include_schema / descriptionPrevious 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." - changed
Input schema / properties / include_subentries / descriptionPrevious 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." - changed
Input schema / properties / limit / descriptionPrevious value: -"Max entries to return per page in list mode (default: 50)"New value: +"Max entries to return per page in list mode" - changed
Input schema / properties / offset / descriptionPrevious value: -"Number of entries to skip for pagination (default: 0)"New value: +"Number of entries to skip for pagination" - changed
Input schema / properties / query / descriptionPrevious 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."
- Changed
ha_get_logs12 fields changed- added
Input schema / properties / compact / descriptionAdded value: +"Logbook only: strip attribute dicts to save context." - added
Input schema / properties / end_time / descriptionAdded value: +"Logbook only: end of the window (ISO datetime)." - added
Input schema / properties / entity_id / descriptionAdded value: +"Logbook only: restrict to this entity." - added
Input schema / properties / hours_back / descriptionAdded value: +"Logbook only: how many hours back to read." - added
Input schema / properties / level / descriptionAdded value: +"system / error_log only: keep only entries at exactly this level (ERROR, WARNING, INFO, DEBUG, CRITICAL); it is not a threshold." - added
Input schema / properties / limit / descriptionAdded value: +"Max entries/lines to return. Does not apply to source='error_log' with structured=True." - changed
Input schema / properties / order / descriptionPrevious 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." - added
Input schema / properties / search / descriptionAdded 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." - added
Input schema / properties / slug / descriptionAdded 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." - added
Input schema / properties / source / descriptionAdded 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." - changed
Input schema / properties / structured / descriptionPrevious 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." - changed
Input schema / properties / top_n / descriptionPrevious 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)."
- Changed
ha_get_operation_status2 fields changed- changed
Input schema / properties / operation_id / descriptionPrevious 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." - added
Input schema / properties / timeout_seconds / descriptionAdded value: +"Seconds to wait for a pending operation to finish before returning its status. 0 returns the current status at once."
- Changed
ha_get_overview7 fields changed- changed
Input schema / properties / detail_level / descriptionPrevious 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." - changed
Input schema / properties / domains / descriptionPrevious 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." - changed
Input schema / properties / fields / descriptionPrevious 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." - changed
Input schema / properties / include_dismissed_repairs / descriptionPrevious 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')." - changed
Input schema / properties / include_notifications / descriptionPrevious value: -"Include active persistent notifications (default: True). Set False to skip."New value: +"Include active persistent notifications." - changed
Input schema / properties / limit / descriptionPrevious 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)." - changed
Input schema / properties / offset / descriptionPrevious value: -"Number of entities to skip for pagination (default: 0)"New value: +"Number of entities to skip for pagination"
- Changed
ha_get_skill_guide5 fields changed- removed
Input schema / properties / file / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - changed
Input schema / properties / file / defaultPrevious value: -nullNew value: +"SKILL.md" - changed
Input schema / properties / file / descriptionPrevious 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." - added
Input schema / properties / file / typeAdded value: +"string" - removed
Input schema / properties / skillRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Skill name from the no-args listing (e.g., 'home-assistant-best-practices')." -}
- Changed
ha_get_state2 fields changed- changed
Input schema / properties / attribute_keys / descriptionPrevious 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." - changed
Input schema / properties / fields / descriptionPrevious 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."
- Changed
ha_get_system_health9 fields changed- added
Input schema / properties / config_entry_id / descriptionAdded value: +"Config entry ID of the integration (find via ha_get_integration). Required when include contains 'diagnostics'." - added
Input schema / properties / device_id / descriptionAdded 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." - added
Input schema / properties / diagnostics_data_limit / descriptionAdded value: +"Pagination window for list-valued diagnostics_data_path results; data becomes {path, items, offset, limit, total, has_more}. Only applies with include='diagnostics'." - added
Input schema / properties / diagnostics_data_offset / descriptionAdded value: +"Pagination start index for list-valued diagnostics_data_path results. Only applies with include='diagnostics'." - added
Input schema / properties / diagnostics_data_path / descriptionAdded 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'." - added
Input schema / properties / diagnostics_fields / descriptionAdded 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'." - added
Input schema / properties / diagnostics_truncate_at_bytes / descriptionAdded 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'." - added
Input schema / properties / include / descriptionAdded 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)." - added
Input schema / properties / include_dismissed_repairs / descriptionAdded 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')."
- Changed
ha_get_todo1 field changed- changed
Input schema / properties / entity_id / descriptionPrevious 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')."
- Changed
ha_get_zone1 field changed- changed
Input schema / properties / zone_id / descriptionPrevious 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)."
- Changed
ha_list_floors_areas2 fields changed- changed
Input schema / properties / area_fields / descriptionPrevious 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." - changed
Input schema / properties / fields / descriptionPrevious 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."
- Changed
ha_list_services5 fields changed- changed
Input schema / properties / detail_level / descriptionPrevious 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." - changed
Input schema / properties / fields / descriptionPrevious 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." - changed
Input schema / properties / limit / descriptionPrevious value: -"Max services to return per page (default: 50)"New value: +"Max services to return per page" - changed
Input schema / properties / offset / descriptionPrevious value: -"Number of services to skip for pagination (default: 0)"New value: +"Number of services to skip for pagination" - changed
Input schema / properties / service_fields / descriptionPrevious 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)."
- Changed
ha_manage_app13 fields changed- changed
Input schema / properties / action / descriptionPrevious 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')." - changed
Input schema / properties / debug / descriptionPrevious 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)." - changed
Input schema / properties / message_limit / descriptionPrevious 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." - changed
Input schema / properties / message_offset / descriptionPrevious 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." - changed
Input schema / properties / method / descriptionPrevious 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." - changed
Input schema / properties / offset / descriptionPrevious 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." - changed
Input schema / properties / path / descriptionPrevious 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." - changed
Input schema / properties / port / descriptionPrevious 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." - changed
Input schema / properties / python_transform / descriptionPrevious 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)]." - changed
Input schema / properties / slug / descriptionPrevious 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)." - changed
Input schema / properties / summarize / descriptionPrevious 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." - changed
Input schema / properties / wait_for_close / descriptionPrevious 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." - changed
Input schema / properties / websocket / descriptionPrevious 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."
- Changed
ha_manage_backup2 fields changed- changed
Input schema / properties / confirm / descriptionPrevious 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." - changed
Input schema / properties / restore_database / descriptionPrevious 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."
- Changed
ha_manage_blueprints1 field changed- changed
Input schema / properties / action / descriptionPrevious 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\")"
- Changed
ha_manage_energy_prefs7 fields changed- changed
Input schema / properties / config / descriptionPrevious 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." - changed
Input schema / properties / config_hash / descriptionPrevious 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." - changed
Input schema / properties / dry_run / descriptionPrevious 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." - changed
Input schema / properties / included_in_stat / descriptionPrevious 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." - changed
Input schema / properties / mode / descriptionPrevious 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." - changed
Input schema / properties / name / descriptionPrevious 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." - changed
Input schema / properties / water / descriptionPrevious 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'."
- Changed
ha_manage_pipeline2 fields changed- changed
Input schema / properties / make_preferred / descriptionPrevious 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." - changed
Input schema / properties / sentence / descriptionPrevious 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'."
- Changed
ha_manage_radio2 fields changed- changed
Input schema / properties / confirm / descriptionPrevious 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)." - changed
Input schema / properties / params / descriptionPrevious 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)."
- Changed
ha_manage_theme4 fields changed- changed
Input schema / properties / action / descriptionPrevious 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'." - changed
Input schema / properties / expected_current / descriptionPrevious 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." - changed
Input schema / properties / force / descriptionPrevious 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." - changed
Input schema / properties / value / descriptionPrevious 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."
- Changed
ha_manage_updates6 fields changed- changed
Input schema / properties / action / descriptionPrevious 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)." - changed
Input schema / properties / backup / descriptionPrevious 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))." - changed
Input schema / properties / categories / descriptionPrevious 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." - changed
Input schema / properties / include_release_notes / descriptionPrevious 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." - changed
Input schema / properties / include_skipped / descriptionPrevious value: -"For list: include updates that have been skipped (default: False)."New value: +"For list: include updates that have been skipped." - added
Input schema / properties / repairsAdded 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." +}
- Changed
ha_reload_core2 fields changed- added
Input schema / properties / entry_id / descriptionAdded 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." - added
Input schema / properties / target / descriptionAdded 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'."
- Changed
ha_remove_entity1 field changed- changed
Input schema / properties / entity_id / descriptionPrevious 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')."
- Changed
ha_remove_helpers_integrations1 field changed- changed
Input schema / properties / target / descriptionPrevious 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."
- Changed
ha_remove_todo_item1 field changed- changed
Input schema / properties / item / descriptionPrevious 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"
- Changed
ha_remove_zone1 field changed- changed
Input schema / properties / zone_id / descriptionPrevious value: -"Zone ID to remove (use ha_get_zone to find IDs)"New value: +"Zone ID to remove"
- Changed
ha_report_issue10 fields changed- added
Input schema / properties / ai_modelAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Your own model identity, as specific as you know it. Do not invent a version." +} - added
Input schema / properties / client_appAdded 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." +} - added
Input schema / properties / descriptionAdded 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." +} - changed
Input schema / properties / fields / descriptionPrevious 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." - added
Input schema / properties / report_typeAdded 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" +} - added
Input schema / properties / titleAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One-line summary of what broke, for the issue title." +} - changed
Input schema / properties / tool_call_count / descriptionPrevious 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." - added
Input schema / properties / tool_callsAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The tool call(s) that produced the problem, verbatim: name, arguments and the (shortened) response." +} - added
Input schema / properties / user_commentAdded 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." +} - added
Input schema / properties / user_promptAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The user message that led to the problem, verbatim." +}
- Changed
ha_restart1 field changed- added
Input schema / properties / confirm / descriptionAdded value: +"Must be True to confirm the restart."
- Changed
ha_search9 fields changed- changed
Input schema / properties / area_filter / descriptionPrevious 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." - changed
Input schema / properties / config_time_budget / descriptionPrevious 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." - changed
Input schema / properties / domain_filter / descriptionPrevious 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')." - changed
Input schema / properties / exact_match / descriptionPrevious 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." - changed
Input schema / properties / include_config / descriptionPrevious 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." - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum results per surface (entities, configs). Default: 10."New value: +"Maximum results per surface (entities, configs)." - changed
Input schema / properties / query / descriptionPrevious 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." - changed
Input schema / properties / result_fields / descriptionPrevious 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." - changed
Input schema / properties / search_types / descriptionPrevious 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."
- Changed
ha_set_area_or_floor5 fields changed- changed
Input schema / properties / floor_id / descriptionPrevious 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)." - changed
Input schema / properties / id / descriptionPrevious 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)" - added
Input schema / properties / labelsAdded 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." +} - changed
Input schema / properties / level / descriptionPrevious 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)." - changed
Input schema / properties / picture / descriptionPrevious 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)."
- Changed
ha_set_device1 field changed- changed
Input schema / properties / disabled_by / descriptionPrevious 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."
- Changed
ha_set_entity16 fields changed- changed
Input schema / properties / aliases / anyOfPrevious value: -[ - { - "type": "string" - }, - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "type": "string" + }, + { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": "array" + }, + { + "type": "null" + } +] - changed
Input schema / properties / aliases / descriptionPrevious 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." - changed
Input schema / properties / area_id / descriptionPrevious 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." - changed
Input schema / properties / categories / descriptionPrevious 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}." - changed
Input schema / properties / device_class / descriptionPrevious 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." - changed
Input schema / properties / enabled / descriptionPrevious 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." - changed
Input schema / properties / entity_id / descriptionPrevious 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." - changed
Input schema / properties / expose_to / descriptionPrevious 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}." - changed
Input schema / properties / hidden / descriptionPrevious 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." - changed
Input schema / properties / icon / descriptionPrevious 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." - changed
Input schema / properties / labels / descriptionPrevious 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." - changed
Input schema / properties / name / descriptionPrevious 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." - changed
Input schema / properties / new_device_name / descriptionPrevious 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." - changed
Input schema / properties / new_entity_id / descriptionPrevious 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." - changed
Input schema / properties / options / descriptionPrevious 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." - added
Input schema / properties / use_entity_name_aliasAdded 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." +}
- Changed
ha_set_integration6 fields changed- changed
Input schema / properties / config / descriptionPrevious 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." - changed
Input schema / properties / confirm_token / descriptionPrevious 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." - changed
Input schema / properties / domain / descriptionPrevious 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')." - changed
Input schema / properties / enabled / descriptionPrevious 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'." - changed
Input schema / properties / entry_id / descriptionPrevious 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." - added
Input schema / properties / log_levelAdded 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." +}
- Changed
ha_set_todo_item2 fields changed- changed
Input schema / properties / item / descriptionPrevious 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." - changed
Input schema / properties / summary / descriptionPrevious 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."
- Changed
ha_set_zone4 fields changed- changed
Input schema / properties / latitude / descriptionPrevious value: -"Latitude coordinate of the zone center (required for create)"New value: +"Latitude coordinate of the zone center" - changed
Input schema / properties / longitude / descriptionPrevious value: -"Longitude coordinate of the zone center (required for create)"New value: +"Longitude coordinate of the zone center" - changed
Input schema / properties / name / descriptionPrevious value: -"Display name for the zone (required for create)"New value: +"Display name for the zone" - changed
Input schema / properties / zone_id / descriptionPrevious 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)"
12 tool updates
v8.5.0- Changed
ha_config_get_dashboard1 field changed- changed
Input schema / properties / force_reload / descriptionPrevious 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."
- Changed
ha_config_set_automation3 fields changed- changed
Input schema / properties / config_hash / descriptionPrevious 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)." - changed
Input schema / properties / identifier / descriptionPrevious 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." - added
Input schema / properties / take_control_of_blueprintAdded 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" +}
- Changed
ha_config_set_dashboard4 fields changed- changed
Input schema / properties / config / descriptionPrevious 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." - changed
Input schema / properties / config_hash / descriptionPrevious 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)." - added
Input schema / properties / patchAdded 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." +} - changed
Input schema / properties / python_transform / descriptionPrevious 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"
- Changed
ha_config_set_script1 field changed- added
Input schema / properties / take_control_of_blueprintAdded 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" +}
- Changed
ha_get_app1 field changed- changed
Input schema / properties / source / descriptionPrevious 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."
- Removed
ha_get_blueprint - Changed
ha_get_logs3 fields changed- changed
Input schema / properties / offset / descriptionPrevious 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." - changed
Input schema / properties / order / descriptionPrevious 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)." - changed
Input schema / properties / source / enumPrevious value: -[ - "logbook", - "system", - "error_log", - "supervisor", - "system_service", - "logger" -]New value: +[ + "logbook", + "system", + "error_log", + "supervisor", + "system_service", + "logger", + "fault_log" +]
- Changed
ha_get_overview1 field changed- changed
Input schema / properties / fields / descriptionPrevious 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."
- Removed
ha_import_blueprint - Changed
ha_manage_app2 fields changed- changed
Input schema / properties / action / descriptionPrevious 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." - changed
Input schema / properties / slug / descriptionPrevious 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."
- Changed
ha_manage_backup1 field changed- changed
Input schema / properties / backup_name / descriptionPrevious 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."
- Added
ha_manage_blueprints
1 tool update
v8.4.3- Changed
ha_search1 field changed- changed
Input schema / properties / config_time_budget / anyOfPrevious value: -[ - { - "exclusiveMinimum": 0, - "maximum": 300, - "type": "number" - }, - { - "type": "null" - } -]New value: +[ + { + "maximum": 300, + "minimum": 0.001, + "type": "number" + }, + { + "type": "null" + } +]
14 tool updates
v8.4.1- Changed
ha_bulk_control13 fields changed- added
Input schema / properties / actionAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "One device action applied to every resolved leaf." +} - added
Input schema / properties / dry_runAdded value: +{ + "default": false, + "type": "boolean" +} - added
Input schema / properties / operations / defaultAdded value: +null - added
Input schema / properties / operations / descriptionAdded 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'." - changed
Input schema / properties / operations / items / additionalPropertiesPrevious value: -trueNew value: +false - added
Input schema / properties / operations / items / descriptionAdded value: +"One entity action in a ha_bulk_control request." - added
Input schema / properties / operations / items / propertiesAdded 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" + } +} - added
Input schema / properties / operations / items / requiredAdded value: +[ + "entity_id", + "action" +] - added
Input schema / properties / parametersAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional action parameters for selector mode." +} - added
Input schema / properties / selectorAdded 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." +} - added
Input schema / properties / timeout_secondsAdded value: +{ + "anyOf": [ + { + "maximum": 60, + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null +} - added
Input schema / properties / validate_firstAdded value: +{ + "default": true, + "type": "boolean" +} - removed
Input schema / requiredRemoved value: -[ - "operations" -]
- Changed
ha_call_service6 fields changed- added
Input schema / properties / data / descriptionAdded 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." - added
Input schema / properties / domain / descriptionAdded value: +"Service domain (e.g. 'light', 'climate', 'automation'). Required for a service call; must be omitted when ws_command is set." - added
Input schema / properties / entity_id / descriptionAdded 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." - added
Input schema / properties / return_response / descriptionAdded 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." - added
Input schema / properties / service / descriptionAdded 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." - added
Input schema / properties / wait / descriptionAdded 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."
- Changed
ha_config_get_dashboard1 field changed- changed
Input schema / properties / include_screenshot / descriptionPrevious 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."
- Changed
ha_config_set_dashboard1 field changed- changed
Input schema / properties / return_screenshot / descriptionPrevious 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."
- Changed
ha_config_set_helper1 field changed- changed
Input schema / properties / config / descriptionPrevious 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."
- Changed
ha_get_app3 fields changed- changed
Input schema / properties / query / descriptionPrevious value: -"Search filter for add-on names/descriptions (only for source='available')"New value: +"App (add-on) name/description filter (only for source='available')" - changed
Input schema / properties / slug / descriptionPrevious 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." - changed
Input schema / properties / source / descriptionPrevious 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."
- Changed
ha_get_logs1 field changed- added
Input schema / properties / offset / descriptionAdded 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."
- Changed
ha_get_operation_status2 fields changed- added
Input schema / properties / timeout_seconds / minimumAdded value: +0 - changed
Input schema / properties / timeout_seconds / typePrevious value: -"integer"New value: +"number"
- Changed
ha_get_overview1 field changed- changed
Input schema / properties / fields / descriptionPrevious 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."
- Changed
ha_manage_app12 fields changed- changed
Input schema / properties / action / descriptionPrevious 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." - changed
Input schema / properties / array_patch / descriptionPrevious 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." - changed
Input schema / properties / auto_update / descriptionPrevious 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)." - changed
Input schema / properties / network / descriptionPrevious 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." - changed
Input schema / properties / options / descriptionPrevious 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)." - changed
Input schema / properties / path / descriptionPrevious 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." - changed
Input schema / properties / port / descriptionPrevious 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." - changed
Input schema / properties / repository / descriptionPrevious 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." - changed
Input schema / properties / request_headers / descriptionPrevious 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." - changed
Input schema / properties / slug / descriptionPrevious 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." - changed
Input schema / properties / wait_for_close / descriptionPrevious 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." - changed
Input schema / properties / websocket / descriptionPrevious 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."
- Changed
ha_manage_backup2 fields changed- changed
Input schema / properties / domain / descriptionPrevious 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." - changed
Input schema / properties / entity_id / descriptionPrevious 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."
- Changed
ha_manage_theme5 fields changed- changed
Input schema / properties / action / descriptionPrevious 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)." - changed
Input schema / properties / action / enumPrevious value: -[ - "list", - "set" -]New value: +[ + "list", + "set", + "get_engine_theme", + "set_engine_theme" +] - added
Input schema / properties / expected_currentAdded 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." +} - added
Input schema / properties / forceAdded 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" +} - added
Input schema / properties / valueAdded 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." +}
- Changed
ha_search4 fields changed- changed
Input schema / properties / area_filter / descriptionPrevious 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." - changed
Input schema / properties / config_time_budget / descriptionPrevious 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." - changed
Input schema / properties / query / descriptionPrevious 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." - changed
Input schema / properties / result_fields / descriptionPrevious 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."
- Changed
ha_set_integration1 field changed- changed
Input schema / properties / config / descriptionPrevious 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."
10 tool updates
v8.3.0- Changed
ha_config_list_helpers1 field changed- changed
Input schema / properties / helper_type / anyOfPrevious 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" + } +]
- Changed
ha_config_set_helper2 fields changed- changed
Input schema / properties / config / descriptionPrevious 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." - changed
Input schema / properties / helper_type / enumPrevious 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" +]
- Removed
ha_get_addon - Added
ha_get_app - Changed
ha_get_logs3 fields changed- changed
Input schema / properties / order / descriptionPrevious 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)." - added
Input schema / properties / structuredAdded 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" +} - added
Input schema / properties / top_nAdded 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." +}
- Removed
ha_manage_addon - Added
ha_manage_app - Changed
ha_manage_pipeline7 fields changed- changed
Input schema / properties / action / descriptionPrevious value: -"Pipeline operation: list, get, create, update, or set_preferred."New value: +"Pipeline operation: list, get, create, update, set_preferred, or process." - changed
Input schema / properties / action / enumPrevious value: -[ - "list", - "get", - "create", - "update", - "set_preferred" -]New value: +[ + "list", + "get", + "create", + "update", + "set_preferred", + "process" +] - added
Input schema / properties / agent_idAdded 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." +} - added
Input schema / properties / conversation_idAdded 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." +} - changed
Input schema / properties / language / descriptionPrevious value: -"Pipeline language, e.g. 'en'."New value: +"Pipeline language, e.g. 'en'. For process, the language to recognise the sentence in." - changed
Input schema / properties / pipeline_id / descriptionPrevious 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." - added
Input schema / properties / sentenceAdded 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." +}
- Changed
ha_remove_helpers_integrations1 field changed- changed
Input schema / properties / helper_type / anyOfPrevious 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" + } +]
- Changed
ha_set_integration6 fields changed- added
Input schema / properties / confirm_tokenAdded 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." +} - added
Input schema / properties / expected_device_idAdded 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." +} - added
Input schema / properties / expected_entity_idsAdded 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." +} - added
Input schema / properties / expected_macAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Requires reconfigure=True. MAC or IEEE the entry's device must still report." +} - added
Input schema / properties / expected_unique_idAdded 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." +} - added
Input schema / properties / reconfigureAdded 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" +}
3 tool updates
v8.2.0- Changed
ha_call_service1 field changed- changed
Input schema / properties / verbose / descriptionPrevious 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."
- Changed
ha_manage_hacs3 fields changed- changed
Input schema / properties / action / descriptionPrevious 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" - changed
Input schema / properties / action / enumPrevious value: -[ - "download", - "add_repository" -]New value: +[ + "download", + "add_repository", + "remove", + "update_information" +] - changed
Input schema / properties / repository_id / descriptionPrevious value: -"Numeric HACS ID or 'owner/repo' path (action='download')"New value: +"Numeric HACS ID or 'owner/repo' path (action='download' / 'remove' / 'update_information')"
- Changed
ha_set_integration1 field changed- changed
Input schema / properties / config / descriptionPrevious 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."
78 tool updates
v7.14.2- First observed
ha_bulk_control - First observed
ha_call_event - First observed
ha_call_service - First observed
ha_config_delete_dashboard - First observed
ha_config_delete_dashboard_resource - First observed
ha_config_get_automation - First observed
ha_config_get_calendar_events - First observed
ha_config_get_category - First observed
ha_config_get_dashboard - First observed
ha_config_get_label - First observed
ha_config_get_scene - First observed
ha_config_get_script - First observed
ha_config_list_dashboard_resources - First observed
ha_config_list_groups - First observed
ha_config_list_helpers - First observed
ha_config_remove_automation - First observed
ha_config_remove_calendar_event - First observed
ha_config_remove_category - First observed
ha_config_remove_group - First observed
ha_config_remove_label - First observed
ha_config_remove_scene - First observed
ha_config_remove_script - First observed
ha_config_set_automation - First observed
ha_config_set_calendar_event - First observed
ha_config_set_category - First observed
ha_config_set_dashboard - First observed
ha_config_set_dashboard_resource - First observed
ha_config_set_group - First observed
ha_config_set_helper - First observed
ha_config_set_label - First observed
ha_config_set_scene - First observed
ha_config_set_script - First observed
ha_eval_template - First observed
ha_get_addon - First observed
ha_get_automation_traces - First observed
ha_get_blueprint - First observed
ha_get_camera_image - First observed
ha_get_device - First observed
ha_get_entity - First observed
ha_get_entity_exposure - First observed
ha_get_hacs_info - First observed
ha_get_history - First observed
ha_get_integration - First observed
ha_get_logs - First observed
ha_get_operation_status - First observed
ha_get_overview - First observed
ha_get_skill_guide - First observed
ha_get_state - First observed
ha_get_system_health - First observed
ha_get_todo - First observed
ha_get_zone - First observed
ha_import_blueprint - First observed
ha_list_floors_areas - First observed
ha_list_services - First observed
ha_manage_addon - First observed
ha_manage_backup - First observed
ha_manage_energy_prefs - First observed
ha_manage_hacs - First observed
ha_manage_pipeline - First observed
ha_manage_radio - First observed
ha_manage_theme - First observed
ha_manage_updates - First observed
ha_reload_core - First observed
ha_remove_area_or_floor - First observed
ha_remove_device - First observed
ha_remove_entity - First observed
ha_remove_helpers_integrations - First observed
ha_remove_todo_item - First observed
ha_remove_zone - First observed
ha_report_issue - First observed
ha_restart - First observed
ha_search - First observed
ha_set_area_or_floor - First observed
ha_set_device - First observed
ha_set_entity - First observed
ha_set_integration - First observed
ha_set_todo_item - First observed
ha_set_zone
TDQS
Scored across 77 tools
Most tools target a distinct resource/action, and descriptions cross-reference alternatives. However, several boundaries are still fuzzy in practice: state vs. registry vs. search, ha_bulk_control vs. ha_call_service, helper groups vs. service groups, and read vs. manage HACS all require careful reading to avoid misselection.
All names use snake_case with a consistent ha_ prefix and mostly verb_noun/resource_action structure. The main deviations are parallel families such as ha_config_set_* vs. ha_set_* for similar resources and ha_manage_* vs. ha_get_*/ha_set_*.
77 tools far exceeds a well-scoped surface (typically 3-15) and is an extreme mismatch even for a broad platform. Home Assistant is wide, but the surface is heavily fragmented into many specialized tools rather than consolidated around core operations.
Coverage spans entities, devices, areas/floors, automations, scripts, scenes, helpers, dashboards/resources, calendars, todo, zones, labels/categories, integrations, apps, HACS, backups, updates/repairs, logs, services, history, traces, radio, energy, and pipelines. No obvious dead ends remain, and ha_call_service fills service-level gaps.
Maintenance
Related MCP Connectors
Hosted MCP server connecting AI assistants to 9,000+ apps and 40,000+ actions via Zapier.
MCP server for AI dialogue using various LLM models via AceDataCloud
A TypeScript MCP server for Home Assistant, enabling programmatic management of entities, automatiโฆ
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yoโฆ
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceAn 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.652MIT
- AlicenseBqualityCmaintenanceMCP server for full Home Assistant control, enabling AI agents to manage dashboards, automations, files, apps, entities, and more via REST API, WebSocket, and SSH.66189 npmMIT
- AlicenseNot gradedqualityCmaintenanceAn MCP server that enables AI assistants to control Home Assistant via natural language, including device control, automation management, and system configuration.MIT
- AlicenseAqualityCmaintenanceA 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.9126 npmMIT