Home Assistant MCP Server
OfficialSummary: An MCP server that lets AI assistants fully control, configure, build, and debug a Home Assistant smart home using natural language.
Search & discovery — fuzzy entity search, config-body search across automations/scripts/scenes/helpers/dashboards, system overview and state queries (
ha_search,ha_get_state,ha_get_overview)Device control — call any service or one-shot WebSocket command, bulk/area/floor control, fire custom events, poll operation status (
ha_call_service,ha_bulk_control,ha_call_event,ha_get_operation_status,ha_list_services)Automations, scripts & scenes — create, read, update, delete, enable/disable, run now, take control of blueprints, with python_transform or full-config edits (
ha_config_*_automation,ha_config_*_script,ha_config_*_scene)Dashboards — list/get/create/update/delete storage-mode dashboards, search cards by entity/type/heading, manage custom card resources, optional screenshot rendering
Helpers — ~30 helper types (input_*, counter, timer, schedule, template, group, utility_meter, statistics, etc.) plus config subentries (
ha_config_list_helpers,ha_config_set_helper)Organization registries — areas, floors, labels, categories, zones, groups, entities and devices (
ha_list_floors_areas,ha_config_*_label,ha_config_*_category,ha_set_entity,ha_set_device,ha_*_zone,ha_config_*_group)Integrations & apps — list/inspect/add/enable/disable/reconfigure integrations, set log levels, manage HACS repos, and install/configure/supervise Home Assistant apps (add-ons), including app API proxying
Calendar & todos — create/update/delete calendar events with recurrence rules; manage todo lists and items
Monitoring & debugging — entity history and long-term statistics, automation/script traces, logs (system, error, supervisor, fault), system health, radio diagnostics (Z-Wave/Zigbee/Matter/Thread), camera snapshots, template/condition evaluation
System administration — backups (full snapshots and per-edit auto-backups with restore/diff), updates and Repairs, restarts and targeted reloads, themes, energy dashboard preferences, blueprints
Safety & governance — Read Only Mode, per-tool enable/disable/pin, tool security policies with user approval, automatic edit backups, best-practices skill guidance, and built-in bug reporting
Tool discovery — full ~87-tool catalog by default, or BM25 search-based discovery (
ha_search_tools+ read/write/delete proxies) for smaller or context-limited models
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.
The Unofficial and Awesome Home Assistant MCP Server

🚀 Get Started
The recommended way to run ha-mcp is the HA-MCP Custom Component. It installs into Home Assistant through HACS, runs the full server in-process, and works on every Home Assistant installation type — Home Assistant OS, Supervised, Container, and Core — with full feature parity. It is the easiest setup in every case, with no access token to manage.
Add it to Home Assistant via HACS (the preferred install):
Quick start:
Install the HA-MCP Custom Component from HACS — click the badge above, or in HACS open Integrations → ⋮ → Custom repositories, add
https://github.com/homeassistant-ai/ha-mcp-integration(category: Integration), then Download.Restart Home Assistant.
Go to Settings → Devices & Services → Add Integration, search for HA-MCP Custom Component, choose HA-MCP Server, and click Submit. Creating the entry starts the server.
Copy the connect URL from the entry's Configure screen (Settings → Devices & Services → HA-MCP Custom Component → HA-MCP Server → Configure) — it is also printed in the Home Assistant log. A notification confirms the server started and points you there.
Paste that URL into your AI client — done.
Connect URL. The Configure screen gives you a Home Assistant webhook URL for remote clients — https://<your-ha-domain>/api/webhook/<webhook-id> through Nabu Casa or any reverse proxy already pointed at Home Assistant (locally, http://<ha-host>:8123/api/webhook/<webhook-id>). For clients on the same network, the server is also reachable directly at http://<ha-ip>:9584/private_<random>.
Replaces other install methods: the in-process server is a complete, standalone ha-mcp install — it takes the place of the app (add-on), Docker, and uvx/PyPI (stdio) methods. Run only one; do not run the in-process server alongside another install.
Local only? Turn off Remote access via webhook in the entry options — no webhook is registered at all, while the direct port and sidebar panel keep working.
Settings panel: while the server runs, an admin-only HA-MCP panel appears in the Home Assistant sidebar for managing tools, feature flags, backups, and themes.
Optional authentication: set Webhook authentication to
ha_authto require a Home Assistant account sign-in instead of using the secret URL as the credential.Manual install (no HACS): copy
custom_components/ha_mcp_tools/from this repository into your Home 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.
🧪 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 Custom Component also powers a set of privileged tools that standard Home Assistant APIs can't provide: file system access and YAML config editing. (The same component runs the full server in-process — that's the recommended install in the Get Started section at the top.) Its File & YAML services entry (HA-MCP File & YAML Tools) enables the tools below.
Tools that require the component:
Tool | Description |
| 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 Custom Component:
To add manually: open HACS > Integrations > three-dot menu > Custom repositories > add https://github.com/homeassistant-ai/ha-mcp-integration (category: Integration) > Download. Or copy custom_components/ha_mcp_tools/ from this repository into your HA config/custom_components/ directory.
After installing, restart Home Assistant, then open Settings > Devices & Services > Add Integration, search for HA-MCP Custom Component, and add the HA-MCP File & YAML Tools entry.
To run the full ha-mcp server in-process through this same component, see the Get Started section at the top and the full in-process server documentation →.
🧠 Better Results with Agent Skills
This server gives your AI agent tools to control Home Assistant. For better configurations, pair it with Home Assistant Agent Skills — domain knowledge that teaches the agent Home Assistant best practices.
An MCP server can create automations, helpers, and dashboards, but it has no opinion on how to structure them. Without domain knowledge, agents tend to over-rely on templates, pick the wrong helper type, or produce automations that are hard to maintain. The skills fill that gap: native constructs over Jinja2 workarounds, correct helper selection, safe refactoring workflows, and proper use of automation modes.
Bundled Skills (built-in)
Skills from homeassistant-ai/skills are bundled and served as MCP resources via skill:// URIs. Any MCP client that supports resources can discover them automatically — no manual installation needed. For tool-only clients (claude.ai, etc.), the best-practices skill is reachable through the ha_get_skill_guide tool: call it with no arguments to read SKILL.md, or with file set to a path SKILL.md links to read that reference file. Resources are not auto-injected into context — clients must explicitly request them, so idle context cost is just the metadata listing.
ha_get_skill_guide is a mandatory tool: the catalog always exposes it (it can't be disabled) so tool-only clients never see a silently missing skill surface.
Skills can still be installed manually for clients that prefer local skill files — see the skills repo for instructions.
🔍 Tool Discovery for AI Agents
By default, the full tool catalog (~87 tools) is listed to the client through the standard MCP tools/list response. Clients with deferred / on-demand tool loading (claude.ai, Claude Desktop, Claude Code) handle that fine — tools are pulled into context only when needed, so idle context cost is near-zero.
For setups without deferred tool support — models like Claude Haiku, Gemini, OpenAI-compatible local models and smaller open-weights models, or clients that inline all tool schemas regardless of model (e.g. GitHub Copilot CLI) — listing the full tool catalog up front adds a lot of idle context and can overwhelm smaller models. To address that, the server ships with a search-based discovery mode built on top of FastMCP's BM25 search transform.
Smaller or local LLMs (Ollama, etc.)
If your model can't see the tools or your Home Assistant, it may be getting handed the whole tool catalog at once and struggling with it. It's recommended to try the following to see if it helps:
Enable tool search (
ENABLE_TOOL_SEARCH=true, or the app option below). Instead of listing every tool up front, the server defers the catalog behind a search interface so the model pulls in only the tools it needs, when it needs them.Raise the model's context window above the default. Local runtimes ship with small defaults (Ollama's
num_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. re-add/refresh the connector in ChatGPT, or close and reopen Claude Desktop). If you skip this, newly enabled tools won't appear in the client at all, and tools the server no longer exposes still show as available but fail when called (Unknown tool, or the Read Only Mode error for a write tool that mode hides). ChatGPT sometimes keeps serving the stale list even after the connector is removed and re-added under the same name — if tools are still missing after re-adding, delete the connector and create a new one with a different name.
For the HA app, the same option is documented in homeassistant-addon/DOCS.md along with the in-app settings UI for fine-grained tool enable/disable/pin.
Read-only HTTP connections
Append /readonly to the server's HTTP MCP endpoint to restrict that connection
to the existing Read Only Mode while other clients keep normal access:
Normal: https://example.com/private_your_secret
Read-only: https://example.com/private_your_secret/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(identifier=..., run_actions=True).
Use this to publish custom event types consumed by event-triggered automations, Node-RED flows, or custom integrations that subscribe to specific event types.
Caveats: Events are fire-and-forget; this tool confirms the event was accepted by the bus but does not verify whether any automation or subscriber acted on it.
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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?
Adds behavior the annotations cannot express: the call is fire-and-forget, success only means the bus accepted the event, and no subscriber action is verified. It also documents a concrete failure mode and recovery (Claude Desktop 4-minute timeout, read the target back, delay manual approval) — genuinely operational context beyond readOnly/destructive/idempotent hints.
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 cleanly partitioned into when-not, when-to, and caveats. The Claude Desktop workaround paragraph is the longest block and is tangential to tool selection, but it is operationally useful rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value explanation is not required, and the description covers purpose, alternatives, and async caveats completely. The one gap is that destructiveHint=true is never reconciled with a tool that merely publishes an event — an agent cannot tell whether the destructiveness comes from the event itself or from downstream automations.
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 (event_type, data) are fully documented in the schema itself, including how data surfaces as trigger.event.data. The description adds no format or syntax detail beyond that, 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+resource ('Execute a custom event on the Home Assistant event bus') and immediately distinguishes itself from the two most confusable siblings by naming them (ha_call_service for entity control, ha_config_set_automation for running automations). An agent can select this tool 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' block names the alternatives and the condition that selects them, and the following paragraph gives the positive use case (event-triggered automations, Node-RED flows, custom integrations). Both when-not and when-to are covered, which is the top of the range.
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 permanently deletes the dashboard and all its configuration. Cannot be undone. Does not work on YAML-mode dashboards.
HA internal IDs may differ from url_path (e.g. hyphens → underscores); the tool resolves either form to the actual registry ID before deletion.
EXAMPLES:
Delete dashboard: ha_config_delete_dashboard("mobile-dashboard")
Note: The default dashboard cannot be deleted via this method.
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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 flag destructiveHint=true and idempotentHint=true, and the description substantially enriches this: permanent, irreversible, all configuration removed, YAML-mode unsupported, default-dashboard exception, and internal-ID vs url_path resolution semantics. It even flags a known timeout/hang workaround, which is behavior no annotation conveys.
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 destructive WARNING are front-loaded, followed by an example and an edge-case note, which is good ordering. The final Claude Desktop paragraph is verbose and narrow in applicability, costing a point on 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 delete with an output schema present, the agent has everything needed: what is destroyed, irreversibility, unsupported targets, ID resolution, and the one undeletable special case. Return values are legitimately omitted since an 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%, so the baseline is 3, but the description adds real value beyond the schema by explaining that HA internal IDs may differ from url_path (hyphens become underscores) and that the tool resolves either form before deletion.
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+scope: 'Delete a storage-mode dashboard completely.' This distinguishes it from siblings such as ha_config_delete_dashboard_resource, ha_config_get_dashboard, and ha_config_set_dashboard without needing 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?
Clear when-not conditions are given: it does not work on YAML-mode dashboards and cannot delete the default dashboard. However, it never names the alternative route for those cases (e.g., resource deletion or editing the YAML config), leaving the fallback 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_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 Home Assistant automation configuration.
Returns the complete configuration including triggers, conditions, actions, and mode settings.
The returned config_hash stays the same across consecutive reads of an unchanged config.
The returned automation_id is the resolved entity_id (canonical
form, e.g. automation.morning_routine) when the registry lookup
succeeds, falling back to the input identifier otherwise.
EXAMPLES:
Get automation: ha_config_get_automation("automation.morning_routine")
Get by unique_id: ha_config_get_automation("my_unique_automation_id")
For comprehensive automation documentation, use ha_get_skill_guide.
read inspect fetch view existing automation config triggers conditions actions get show detail
| Name | Required | Description | Default |
|---|---|---|---|
| identifier | Yes | Automation entity_id (e.g., 'automation.morning_routine') or unique_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, yet the description adds genuinely new behavior: config_hash is stable across consecutive reads of an unchanged config (a caching/idempotency signal), and automation_id resolution semantics with a fallback to the input identifier. This is context beyond the structured fields, though no error/not-found behavior is described.
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 return contents, then two examples – a clean progression. The trailing keyword blob ('read inspect fetch view ... get show detail') is pure stuffing that earns no place and drags an otherwise tight description down slightly.
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-value documentation is not owed, and the description still covers the parts an agent needs (config_hash stability, id resolution). For a single-parameter, read-only getter with full annotation and schema coverage, 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 the baseline is 3, but the description adds meaning by confirming identifier accepts either an entity_id or a unique_id and by explaining how that identifier is resolved into the returned automation_id. That resolution/fallback rule is not derivable from the schema alone.
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 automation configuration') and enumerates what the returned config contains (triggers, conditions, actions, mode). The 'automation' resource cleanly separates it from the many sibling tools like ha_config_get_script, ha_config_get_scene, and ha_config_set_automation without needing 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?
Two worked examples show exactly what to pass (entity_id vs unique_id), giving clear invocation context, and it routes documentation questions to ha_get_skill_guide. It stops short of explicit exclusions, e.g. when to prefer ha_get_entity or ha_get_state over this, so it is clear-but-not-complete.
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 (default: now) | |
| 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 declare readOnlyHint/idempotentHint/destructiveHint=false, so the safety profile is covered. The description adds real context beyond that: which fields are returned, the conditional presence of uid/recurrence_id/rrule, and the default 'next week' window. It does not cover pagination or what happens when max_results is exceeded, 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?
Front-loads the purpose in one sentence, then return shape, then entity-discovery routing, then examples. Every sentence carries information an agent needs, with 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 documents return values, annotations cover the safety profile, and all four parameters are described. Combined with the return-field list and entity-discovery pointer, nothing needed to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 entity_id, start, end, and max_results, including the important 'end defaults to 7 days from now, not from start' caveat. The description restates the default window only loosely ('next week') and adds no syntax or format detail beyond the schema, 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 (get) and resource (calendar events) with an explicit scope (within a time range) and names the entity type it reads from. An agent can distinguish it from the set/remove calendar event 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?
Explicitly routes entity discovery to ha_search(domain_filter='calendar') and points out that the edit/remove tools consume the uid and recurrence_id this tool returns, telling the agent when to reach for the sibling tools. Two concrete examples cover the default and date-range invocations.
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. 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.
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?
Annotations establish the safety profile (readOnly, idempotent, non-destructive), and the description adds substantial context beyond them: 200-match truncation via `truncated`, cache-invariant config_hash, YAML/strategy/default-dashboard search exclusions, and view_path error behavior. It is rich, though a full accounting of every caveat is impossible to verify, so it stops short of a clean 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 opening line front-loads the three modes and the exclusions before the detailed mode breakdown and caveats, and the examples are cleanly separated. It is long, but for an 11-parameter, multi-mode tool nearly every sentence carries load; density is the only minor cost.
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 carries the return-value burden and does so: it names matched values, jq_path/python_path, config_hash, view/view_index, and truncated. Combined with the caveats section, an agent has everything needed to call and interpret this tool across all modes.
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 are already complete and the baseline would be 3. The description nevertheless adds cross-parameter semantics the schema does not: that search criteria are AND-ed, that list_only is ignored when a search parameter is given, and how each mode is triggered. That mode-selection logic genuinely exceeds the individual schema 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?
Opens with a specific verb+resource: 'Get Lovelace dashboards' and enumerates its three modes (list, read one config, search cards). It explicitly distinguishes itself from siblings ha_get_dashboard_screenshot and ha_search, so an agent can place 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?
Provides explicit when-not guidance ('Not for a render alone... Not for entity discovery: use ha_search') and explains that the parameters select the mode, with examples covering each mode. The alternatives and selection conditions are stated, not inferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_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 via state lookup; scripts strip the prefix).
EXAMPLES:
Get script (bare form): ha_config_get_script("morning_routine")
Get script (entity_id form): ha_config_get_script("script.morning_routine")
For detailed script configuration help, use ha_get_skill_guide.
read inspect fetch view existing script config sequence actions get show detail
| 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 readOnly/idempotent/non-destructive, so the safety profile is covered; the description adds genuinely new behavioral context beyond that — config_hash stability across consecutive reads and the canonical script_id resolution with its fallback path. No auth or rate-limit detail, so not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and return contents are correctly front-loaded, but the REST-envelope fallback paragraph is deep internal detail and the trailing keyword dump ('read inspect fetch view existing script config sequence actions get show detail') is pure filler that dilutes 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 an output schema present, return-value documentation is not required, yet the description still explains the two notable return fields (config_hash, script_id) and their interpretation. For a read-only single-parameter tool, 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 coverage is 100% (baseline 3), and the description goes further by documenting the remote-resolution semantics of script_id: it is the canonical bare storage key that set/remove expect, matching get_automation's prefix handling, with two worked examples of valid input forms.
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 script configuration') and immediately enumerates what the config contains (sequence, mode, fields). It is clearly separable from siblings ha_config_set_script / ha_config_remove_script, which it names and ties back to via the returned script_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?
Provides clear context for when this is the right call (reading an existing script's full config) and routes the agent to an alternative for a related need ('For detailed script configuration help, use ha_get_skill_guide'). It does not state explicit when-not conditions or contrast against ha_config_get_automation beyond a parity note, 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_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.
EXAMPLES:
Next page: ha_config_list_dashboard_resources(offset=100)
List with full content: ha_config_list_dashboard_resources(include_content=True)
Home Assistant 2026.6+ exposes resource management in the UI by default; API access works regardless of UI availability.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max resources to return per page | |
| offset | No | Number of resources to skip for pagination | |
| 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?
Annotations already establish the safe read profile (readOnlyHint, idempotentHint, destructiveHint=false), and the description usefully adds pagination semantics (total_count/has_more vs per-page), the token-saving inline preview behavior, and the downstream ID contract for update/delete. Some of the return-field explanation overlaps the existing output schema, so it is strong but not maximally additive.
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-shape and pagination detail, then compact examples. The closing note about Home Assistant 2026.6+ UI availability is somewhat tangential, keeping it just short of 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?
With an output schema carrying return values and annotations covering the safety profile, the description only needed to fill pagination, preview/truncation, and ID-usage gaps — all of which it does. Nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all three parameters are already documented in the schema, including defaults, bounds, and the truncation caveat. The description's examples reinforce offset/include_content usage but add little semantic detail beyond what the schema already supplies, 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 and resource ('List Lovelace dashboard resources') and immediately scopes what a resource is (custom cards, themes, CSS/JS), which distinguishes it cleanly from the sibling set/delete dashboard resource tools. An agent can identify the tool's role 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 concrete usage examples (offset=100 for the next page, include_content=True for full content) and explains the pagination workflow, plus the pointer that each resource has an ID used for update/delete. It stops short of explicitly naming when NOT to use it or routing to a specific sibling, so it is clear context but not full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_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 includes its entity_id, friendly name, state,
member entities, icon and all 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 readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so the safety profile is covered. The description adds real behavioral context beyond that: pagination semantics (one page returned, total_count and has_more report the full set) and the exact scope of what is returned versus platform-specific groups.
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 scope, field contents, and a concrete example in roughly four compact sentences. The field enumeration (entity_id, name, state, members, icon, all mode) is slightly list-heavy but still earns its place by telling the agent what a group record looks like.
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 the return-value detail is somewhat redundant, but the pagination framing (total_count/has_more) and the old-style-group scope are the key things an agent needs to call and interpret this correctly. Nothing essential is missing for a read-only paginated list 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 limit and offset are already documented in the schema and the baseline is 3. The trailing EXAMPLE demonstrates offset in use, which is mildly reinforcing, but adds no format or constraint detail the schema does not already carry.
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 clear verb+resource (list Home Assistant entity groups with member entities) and sharpens the scope by excluding platform-specific groups like light or cover groups, which are separate entities. It does not name a specific sibling tool (e.g. ha_config_list_helpers or ha_get_entity) as an alternative, so it falls just short of full sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: the note that only old-style groups from group.set/YAML are returned tells the agent the result set is narrower than all group-like entities, and the example hints at pagination. There is no explicit when-to-use, when-not, or named alternative tool, leaving the agent to infer routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_config_list_helpersList 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'. |
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), yet the description adds substantial behavioral context: pagination via total_count/has_more, the COMPONENT_NOT_INSTALLED error conditions, degraded-registry warnings, withheld options, and the person YAML 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?
Content is front-loaded and the examples section is useful, but the description is over-long with redundancy (COMPONENT_NOT_INSTALLED stated twice) and ends with an awkward keyword-stuffed tail ('list all helpers input_boolean input_number ...') that does not 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?
Given six parameters, an output schema, and the type/degradation edge cases, the description covers what an agent needs: record fields, pagination, error modes, and the describe workflow. Nothing essential 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 real meaning beyond the schema: helper_type='all' is component-only and errors rather than returning partial results, and describe/helper_id/menu_choice are tied to the set_helper 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 states a specific verb+resource with scope: 'List Home Assistant helpers of a specific type with their configurations.' It is unambiguous versus write siblings like ha_config_set_helper and is clearly a list/read tool.
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 context ('Call it before ha_config_set_helper to learn the config keys for a type') and routes to ha_get_skill_guide for deeper docs. It does not, however, state when NOT to use it or contrast it against near-neighbors like ha_config_list_groups.
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.
The returned automation_id is the resolved entity_id (canonical
form, e.g. automation.morning_routine) when the registry lookup
succeeded before the delete, falling back to the input
identifier otherwise.
EXAMPLES:
Delete automation: ha_config_remove_automation("automation.old_automation")
Delete by unique_id: ha_config_remove_automation("my_unique_id")
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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. | |
| identifier | Yes | Automation entity_id (e.g., 'automation.old_automation') or unique_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 and idempotentHint=true, so the safety profile is covered. The description adds real value beyond them: it emphasizes deletion is permanent, explains the returned automation_id resolution/fallback behavior, and documents a known Claude Desktop timeout hang. It stops short of stating required permissions or auth needs.
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 leads, followed by return semantics and compact examples, so it is well front-loaded. The trailing Claude Desktop timeout workaround is a fairly dense tangent that lengthens the definition, though it is framed as actionable and call-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?
With an output schema present the description needn't explain return values, yet it usefully clarifies the automation_id resolution anyway. Between annotations covering destructiveness/idempotency and examples covering argument forms, an agent has what it needs to call this 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 both parameters are already documented in the schema. The description reinforces the dual identifier semantics (entity_id or unique_id) and its fallback return behavior but adds little syntax or format detail beyond the schema, matching the baseline for fully-covered schemas.
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 (Delete) and resource (Home Assistant automation) plus the permanence qualifier, making it easy to distinguish from siblings like ha_config_get_automation and ha_config_set_automation 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?
The EXAMPLES block gives concrete invocation patterns (by entity_id and by unique_id) that clarify when to reach for it, and 'permanently' implies this is not the tool for reversible toggling. However, it never explicitly names an alternative (e.g. disable vs delete) or states when not to use it.
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 will reappear after restart.
This only removes old-style groups, not platform-specific groups.
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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 without '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 only declare destructive/idempotent/readOnly state; the description adds consequences the agent could not infer: automations referencing the group may break, YAML-defined groups reappear after restart, and platform-specific groups are out of scope. It also documents an operational failure mode (Claude Desktop 4-minute hang and the read-back-before-repeat mitigation), which is meaningful behavioral context 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?
Bold headers and front-loaded purpose make it scannable, and the routing guidance is prioritized correctly. The Claude Desktop workaround paragraph is three sentences of client-specific advice that dilutes an otherwise tight definition, which keeps it out of 5 territory.
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 scope limits, persistence behavior, side effects, discovery tooling, and environment-specific failure handling. Nothing needed to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the schema already documents both object_id (with the 'group.' prefix caveat) and the wait flag, so the baseline is 3. The description only reinforces object_id via an example call and adds nothing about the wait parameter or removal timing semantics.
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 separates itself from the config-entry-backed siblings it must not be confused with. An agent can distinguish it from ha_remove_helpers_integrations, ha_config_set_group, and 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?
Contains explicit 'When to use' and 'When NOT to use' sections naming the alternative tool (ha_remove_helpers_integrations) and the exact condition that selects it (helper_type="group" groups). It also points to ha_config_list_groups() for discovery, so both the routing decision and the prerequisite lookup are spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_config_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
identifier turns the automation on or off without touching config):
python_transform: surgical edits to an existing automation. Requires identifier and config_hash from ha_config_get_automation(). Operates on the fetched config, which uses HA's plural root keys 'triggers'/'actions'/'conditions', e.g. python_transform="config['triggers'].append({'trigger': 'state', 'entity_id': 'binary_sensor.motion', 'to': 'on'})"
config: new automations or full restructures. Regular automations need alias, triggers and actions; blueprint automations need alias and use_blueprint {path, input}.
take_control_of_blueprint: convert a blueprint-backed automation into a standalone one. Takes no config of its own.
IDENTITY: omit identifier and config['id'] to create with a generated ID;
an unused raw ID creates that specific ID. Reusing an identifier targets
the same automation even if the alias changes; to rename or replace it,
read it first and pass its config_hash — a changed alias without that
hash is rejected before writing. The returned automation_id is the
resolved entity_id, falling back to the input identifier or the
generated unique_id.
EXAMPLES:
Create: ha_config_set_automation(config={"alias": "Morning Lights", "triggers": [{"trigger": "time", "at": "07:00:00"}], "actions": [{"action": "light.turn_on", "target": {"area_id": "bedroom"}}]})
From a blueprint: ha_config_set_automation(config={"alias": "Motion Light Kitchen", "use_blueprint": {"path": "homeassistant/motion_light.yaml", "input": {"motion_entity": "binary_sensor.kitchen_motion", "light_target": {"entity_id": "light.kitchen"}}}})
Update: current = ha_config_get_automation(identifier="automation.x"); ha_config_set_automation(identifier="automation.x", config_hash=current["config_hash"], config={...})
Disable: ha_config_set_automation(identifier="automation.x", enabled=False)
Run now: ha_config_set_automation(identifier="automation.x", run_actions=True)
Take control: ha_config_set_automation(identifier="automation.x", take_control_of_blueprint=True)
TAKE CONTROL is one-way: later blueprint edits stop reaching the
automation; to change an input value, update 'use_blueprint.input'
instead. It does NOT free the blueprint — Home Assistant keeps counting
the converted automation as a user, so deleting that blueprint stays
refused until the automation is removed (an automation reload does not
clear it). The response names the blueprint in
took_control_of_blueprint. To preview the rendering without writing
anything, use ha_manage_blueprints(action="substitute", path=..., input=...).
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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. 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. | |
| identifier | No | 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. | |
| 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 identifier 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 - Dict methods: update, get, setdefault, keys, values, items - Loops: for, if/else, pass, break, continue - Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...) - Ternary: x if condition else y - Iterable unpacking (* in calls/literals): f(*xs), [*xs, y] - Dict unpacking (**) in calls and dict literals: {**d, 'k': v} - Keyword arguments: func(key=value) - Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score']) - String methods: startswith, endswith, lower, upper, strip, split, join, replace - Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed, min, max, sum, abs, any, all, round, str, int, float, bool, list, dict, tuple, set ❌ FORBIDDEN: - Imports: import, from, __import__ - File operations: open, read, write - Dunder access: __class__, __bases__, __subclasses__ - Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr - Function definitions: def, class - Exception handling: try/except (validate with isinstance/in/.get() instead) - While loops: use bounded for loops or comprehensions instead | |
| take_control_of_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 identifier; 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 already declare destructiveHint=true and idempotentHint=false, but the description adds far more: take-control is one-way, the blueprint stays locked as a user until the automation is removed, alias changes require config_hash, and there is a documented Claude Desktop timeout workaround. That is substantial context 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?
Front-loaded purpose, clear section headers (MODES, IDENTITY, EXAMPLES, TAKE CONTROL), and every paragraph carries decision-relevant content. It is long for a description, and the trailing keyword list ("create update modify edit...") is search padding that does not earn its place, but the structure prevents it from being unusable.
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, no-required-field tool with an output schema present, the description covers every path an agent needs: creation, update, rename/locking, mode selection, enable/disable, run-now, and blueprint conversion. Nothing material 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 91%, so the baseline is 3, but the description meaningfully adds identity resolution semantics (omit identifier/config['id'] to generate an ID; unused raw IDs are literal) and the mode-to-parameter requirements, which the schema only partially conveys. It stops short of 5 because the deep per-parameter detail (e.g. python_transform security rules) lives in the schema, not the 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?
Opens with a precise verb+resource ("Create or update a Home Assistant automation") and immediately differentiates itself from siblings by routing to ha_config_set_scene, ha_config_set_helper, and ha_eval_template for adjacent cases. An agent can tell exactly what this tool is for 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 enumerates the three modes (python_transform/config/take_control_of_blueprint) with the conditions that select each, states when a dedicated sibling is preferable, and calls out mutual exclusivity. When-to-use and when-to-use-something-else are both spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_config_set_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'). Defaults to 'mdi:view-dashboard' | |
| 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 | Dashboard display name shown in sidebar | |
| 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. New dashboards must use a hyphenated path. | |
| 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 - Dict methods: update, get, setdefault, keys, values, items - Loops: for, if/else, pass, break, continue - Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...) - Ternary: x if condition else y - Iterable unpacking (* in calls/literals): f(*xs), [*xs, y] - Dict unpacking (**) in calls and dict literals: {**d, 'k': v} - Keyword arguments: func(key=value) - Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score']) - String methods: startswith, endswith, lower, upper, strip, split, join, replace - Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed, min, max, sum, abs, any, all, round, str, int, float, bool, list, dict, tuple, set ❌ FORBIDDEN: - Imports: import, from, __import__ - File operations: open, read, write - Dunder access: __class__, __bases__, __subclasses__ - Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr - Function definitions: def, class - Exception handling: try/except (validate with isinstance/in/.get() instead) - While loops: use bounded for loops or comprehensions instead | |
| return_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 declare destructiveHint=true, idempotentHint=false and openWorldHint=true, and the description adds substantial context beyond them: config 'Replaces everything', strategy dashboards cannot be converted here, config_hash provides optimistic locking, metadata updates are rejected when combined with patch, and the Claude Desktop timeout behavior. This is far richer than 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?
Long but front-loaded (purpose, prerequisite, then modes) and nearly every sentence carries operational weight, including the worked EXAMPLE and the mode decision list. Some content, such as the python_transform security pointer and mutual-exclusion notes, overlaps the schema, keeping it 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?
For a 13-parameter, multi-mode, destructive write tool with no output schema, the description covers prerequisites, mode selection, hash/locking requirements, metadata-update constraints, scope limits (storage vs YAML mode), post-write verification, and even a known client hang. 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 coverage is 92%, so the schema already carries most per-parameter meaning (baseline 3). The description adds a layer the schema does not: the mutual-exclusion relationships framed as a mode-selection decision, when config_hash is required vs optional, and that patch values are literal. It stops short of adding new syntax detail 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?
Opens with a specific verb+resource ('Create or update a Home Assistant dashboard') and immediately distinguishes the three operating modes (patch, python_transform, config) and the storage-mode-only scope. It also names the sibling tools it is not (ha_config_get_dashboard, ha_config_delete_dashboard, ha_config_set_yaml), 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?
Explicit decision framework: 'MODES (pick one)' with the condition selecting each, a mandatory prerequisite (call ha_get_skill_guide first), guidance to fetch entity IDs via ha_search/ha_get_overview rather than guessing, and named alternatives for follow-up (ha_get_dashboard_screenshot) and for YAML-mode dashboards (ha_config_set_yaml / UI 'Take Control'). When-not-to-use 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_dashboard_resourceSet Dashboard ResourceADestructive
Create or update a dashboard resource (inline code or external URL).
Provide exactly one of:
content: Inline JavaScript or CSS code (embedded in the resource URL as a data: URI — no file storage or external hosting involved). Supports 'module' and 'css' types only (not 'js'); URLs are deterministic (same content = same URL); up to ~128KB. Content must be self-contained: a data: URI has no base URL, so relative imports inside a module and relative url() references inside CSS cannot resolve (use fully-qualified URLs instead). If Home Assistant is behind a reverse proxy that injects a Content-Security-Policy without 'data:' in script-src/style-src, the browser blocks these resources: this call still succeeds and the card simply never renders. Register the code as a file and use url='/local/...' on such a deployment. (HA itself ships no CSP.)
url: External resource URL — /local/... (files in /config/www/), /hacsfiles/... (HACS-installed cards) or https://... (CDN). Supports all types: 'module', 'js', 'css'.
EXAMPLES:
Inline: ha_config_set_dashboard_resource(content="class MyCard extends HTMLElement { ... } customElements.define('my-card', MyCard);", resource_type="module")
HACS card (after ha_manage_hacs(action='download')): ha_config_set_dashboard_resource(url="/hacsfiles/lovelace-mushroom/mushroom.js", resource_type="module")
Update: ha_config_set_dashboard_resource(url="/local/my-card-v2.js", resource_type="module", resource_id="abc123")
After adding a resource, a browser cache clear or hard refresh (Ctrl+Shift+R) is needed to load it.
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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. | |
| content | No | JavaScript or CSS code to host inline. | |
| 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: 'module' for ES6 modules (modern cards), 'js' for legacy JavaScript (older custom cards), 'css' for stylesheets (themes, global styles) | module |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only flag destructive=true/idempotent=false; the description adds substantial context beyond them: data: URI embedding with no file storage, deterministic URLs, ~128KB cap, self-containment requirements, the silent-failure mode under a CSP proxy ('this call still succeeds and the card simply never renders'), the required browser cache clear, and the 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?
Front-loaded with purpose, then the content/url branches, examples, and caveats. Most sentences carry real constraint information, but the trailing Claude Desktop timeout workaround and the long CSP digression make it heavier than it needs to be for 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 mutation tool with subtle semantics, the description covers the create-vs-update path, both input modes, type restrictions, failure modes, and post-call refresh needs; an output schema exists so return values need no explanation. Nothing an agent needs to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds constraints the schema cannot express: the exactly-one-of relationship between content and url (the schema permits all-null), the fact that content supports only 'module' and 'css' and not 'js', and the size limit on inline content.
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: 'Create or update a dashboard resource (inline code or external URL)', immediately scoping it to dashboard resources rather than the dashboard itself (ha_config_set_dashboard) or the resource list/delete siblings. The inline-vs-URL distinction further pins down what the tool operates on.
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 strong when-to-use rule ('Provide exactly one of: content or url'), explains the choice between them, and gives a when-not with a concrete alternative (on CSP-injecting reverse proxies, register the code as a file and use url='/local/...'). It does not route to sibling tools (e.g., listing resources for IDs or deleting), which keeps it 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_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.
For NEW groups: Provide object_id and entities (required). For EXISTING groups: Provide object_id and any fields to update. entities, add_entities, and remove_entities are mutually exclusive.
EXAMPLES:
Create: ha_config_set_group("bedroom_lights", entities=["light.lamp", "light.ceiling"], name="Bedroom Lights")
Add entities: ha_config_set_group("lights", add_entities=["light.extra"])
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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 for the group | |
| 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 | |
| entities | No | List of entity IDs for the group. When updating, replaces all entities. | |
| object_id | Yes | Group identifier without 'group.' prefix (e.g., 'living_room_lights') | |
| add_entities | No | Add these entities to an existing group | |
| remove_entities | No | Remove these entities from an existing group |
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, readOnlyHint=false, idempotentHint=false, and openWorldHint=false. The description adds lifecycle facts not in annotations: groups created here are only removable via ha_config_remove_group and invisible to ha_remove_helpers_integrations, and config-entry-backed alternatives behave differently. It also warns about a Claude Desktop timeout/hang workaround, which is valuable operational context 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?
Front-loaded purpose followed by bolded when-not/when-to-use sections, parameter rules, and examples. The Claude Desktop timeout paragraph is longer than typical but is targeted operational guidance; overall there is little filler, though the description is more verbose 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?
Covers creation and update modes, alternatives, lifecycle/removal path, mutual exclusivity, and examples. An output schema exists, so return values need not be explained. Complete for a destructive 8-parameter config tool with complex sibling alternatives.
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 conditional semantics—object_id plus entities required for new groups, any fields for updates, and mutual exclusivity among entities/add_entities/remove_entities—which goes beyond the schema's per-field descriptions. It does not clarify the roles of wait, all_on, or icon beyond what the schema already says.
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 ('service-based Home Assistant entity group via the group.set service'). It distinguishes the tool from the sibling ha_config_set_helper and explains the alternative. An agent can identify scope 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 names when NOT to use (prefer ha_config_set_helper) and when to use (compatibility with existing group.set/YAML groups, rare case where entity-registry membership is unwanted). It also states mutual exclusivity of entities/add_entities/remove_entities and the different required fields for new versus existing groups. Examples reinforce correct invocation.
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, set at creation or by ha_set_zone; ha_get_zone shows it), which cannot be removed | |
| 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 | Bare ID ('my_button') or 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?
Far exceeds the annotations (which only say non-read-only, destructive, non-idempotent). It discloses patch semantics on update (omitted fields are preserved), the action-fallback discriminator, silent ignoring of undeclared flow keys, that validation errors echo data_schema/menu_options for self-correction, and the Claude Desktop timeout workaround. This is unusually rich operational context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose then logically sectioned by helper category, EXAMPLES, and behavior. Prose is dense but mostly earns its place for a 16-param polymorphic tool. The trailing keyword-stuffing line is pure noise and the Claude Desktop workaround paragraph is tangential, 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?
For a highly complex, multi-mode mutation tool, the description covers the create/update/reconfigure branches, per-type field requirements, and self-correction via returned data_schema. An output schema exists, so return-value explanation is correctly omitted, and 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 description coverage is already 94%, so the baseline is 3, but the description adds real meaning beyond it: the per-type config key inventory (input_number min/max/step, zone lat/long/radius, etc.), the step_values escaping mechanism for multi-step flows, and the null-clears-a-field rule. It stops short of clarifying a few of the 16 params (labels/area_id/category are only defined in the schema), so 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+resource ('Create or update Home Assistant helpers') and immediately partitions the space into SIMPLE types, FLOW types, and CONFIG_SUBENTRY, each with its own mechanics. An agent can distinguish this from siblings like ha_config_set_script or ha_config_list_helpers 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 prerequisites ('MUST call ha_get_skill_guide OR refer to locally installed skills first'), a when-not rule (do not create `otp` here because the user enrolls it in an authenticator app), and routes to alternatives for follow-up (ha_config_list_helpers(describe=True), ha_set_entity for renaming). Nothing about tool selection is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_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 - Dict methods: update, get, setdefault, keys, values, items - Loops: for, if/else, pass, break, continue - Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...) - Ternary: x if condition else y - Iterable unpacking (* in calls/literals): f(*xs), [*xs, y] - Dict unpacking (**) in calls and dict literals: {**d, 'k': v} - Keyword arguments: func(key=value) - Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score']) - String methods: startswith, endswith, lower, upper, strip, split, join, replace - Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed, min, max, sum, abs, any, all, round, str, int, float, bool, list, dict, tuple, set ❌ FORBIDDEN: - Imports: import, from, __import__ - File operations: open, read, write - Dunder access: __class__, __bases__, __subclasses__ - Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr - Function definitions: def, class - Exception handling: try/except (validate with isinstance/in/.get() instead) - While loops: use bounded for loops or comprehensions instead |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true, readOnlyHint=false, and idempotentHint=false, and the description reinforces this by stating 'full config replacement' and 'wholesale replacement'. Beyond that it discloses a hard prerequisite (MUST call ha_get_skill_guide first), optimistic-locking semantics (config_hash REQUIRED for python_transform), mutual exclusivity of modes, activation after reload, and a known Claude Desktop timeout/hang workaround — none of which is in 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 purpose followed by clearly labeled WHEN TO USE / WHEN NOT TO USE / SHAPE / EXAMPLE blocks makes it scannable despite its length. It is somewhat verbose — the Claude Desktop timeout workaround is a multi-sentence tangent and it ends with a keyword-tail ('create update modify edit scene ...') that adds bulk without semantic clarity.
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. For a 9-parameter, dual-mode mutation tool the description covers prerequisites, mode selection, locking, examples, and security constraints for the executable field — everything 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 description coverage is already 89%, so the schema carries most parameter meaning (including a detailed python_transform security allow/forbid list and config_hash purpose). The description still adds value the schema lacks: a concrete shape contract pointer and a worked example showing the 'entities' dict keyed by entity_id, plus the clarification that 'activate' can be passed alone with scene_id.
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 or update a Home Assistant scene') and immediately decomposes it into three distinct modes (full config replacement, python_transform, activate-only). It explicitly names the sibling it is not for ('To list or look up existing scenes, use ha_config_get_scene'), so an agent can differentiate 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?
Contains explicit WHEN TO USE and WHEN NOT TO USE sections that map each mode to a scenario ('python_transform' for surgical single-entity edits, 'config' for creation/wholesale replacement, 'activate' alone with scene_id). The exclusion routes to the correct alternative tool (ha_config_get_scene). 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_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 - Dict methods: update, get, setdefault, keys, values, items - Loops: for, if/else, pass, break, continue - Comprehensions: [x for x in ...], {k: v for ...}, (x for x in ...) - Ternary: x if condition else y - Iterable unpacking (* in calls/literals): f(*xs), [*xs, y] - Dict unpacking (**) in calls and dict literals: {**d, 'k': v} - Keyword arguments: func(key=value) - Lambdas (e.g. for `key=`): sorted(items, key=lambda x: x['score']) - String methods: startswith, endswith, lower, upper, strip, split, join, replace - Safe builtins: isinstance, len, range, enumerate, zip, sorted, reversed, min, max, sum, abs, any, all, round, str, int, float, bool, list, dict, tuple, set ❌ FORBIDDEN: - Imports: import, from, __import__ - File operations: open, read, write - Dunder access: __class__, __bases__, __subclasses__ - Dangerous builtins: eval, exec, compile, getattr, setattr, delattr, hasattr - Function definitions: def, class - Exception handling: try/except (validate with isinstance/in/.get() instead) - While loops: use bounded for loops or comprehensions instead | |
| take_control_of_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?
Beyond the annotations (destructiveHint=true, idempotentHint=false), the description discloses real behavioral traits: take-control is one-way, it does NOT free the blueprint and HA keeps counting the converted script as a user so blueprint deletion stays refused, config writes trigger a reload with no completion signal, and the Claude Desktop timeout workaround. This is exactly the kind of context annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and prerequisite are front-loaded, then modes, examples, and edge cases follow in a logical order. It is long, and the best-practice/template guidance paragraph partly duplicates content that ha_get_skill_guide serves, but for an 11-parameter, 4-mode tool the length is largely earned.
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 four operating modes, an output schema (so return values need not be described), and rich annotations, the description covers modes, prerequisites, side effects, examples, and even a known client failure mode. An agent has everything needed 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 already 91%, so the schema carries most parameter meaning; the description adds mode-level semantics that the schema does not (python_transform requires config_hash, run must be used alone with script_id, mutual exclusivity between modes). It stops short of documenting every parameter (e.g. wait, MandatoryBPS, category), which the schema 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?
The first sentence states a specific verb+resource ('Create or update a Home Assistant script'), and the description explicitly distinguishes itself from the sibling ha_config_set_automation ('for trigger-based execution use ha_config_set_automation') and clarifies that scripts use 'sequence', not 'trigger'/'action'. An agent can differentiate it from adjacent config 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?
It gives explicit when-to-use routing via a MODES block (python_transform vs config vs take_control_of_blueprint vs run), states the required prerequisite ('MUST call ha_get_skill_guide OR refer to your locally installed skills first'), and names the alternative tool for previewing blueprint rendering (ha_manage_blueprints). 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_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.
Use action="search" to search/browse/list store repositories, or
action="info" for one repository's full details (README, versions, GitHub
stats). This tool is read-only; to install or add repositories use
ha_manage_hacs, and for non-HACS entities/config use the domain-specific tools.
DASHBOARD TIP: action="search", installed_only=True, category="lovelace"
discovers installed custom cards to wire into ha_config_set_dashboard().
Examples:
Search the store: ha_get_hacs_info(action="search", query="mushroom", category="lovelace")
List installed: ha_get_hacs_info(action="search", installed_only=True)
Repository details: ha_get_hacs_info(action="info", repository_id="441028036")
Caveats: info fetches full repository detail from GitHub, so it can hit GitHub
rate limits / needs HACS's configured GitHub token; search reads HACS's locally
cached repository index.
| 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') | |
| 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') |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish read-only, idempotent, non-destructive behavior, but the description adds meaningful operational context: info fetches from GitHub and may hit rate limits or need a configured GitHub token, while search reads HACS's local cache. It also clarifies the mutation boundary by directing install/add actions to ha_manage_hacs.
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 routes between modes, names alternatives, adds a cross-tool dashboard tip, provides examples, and closes with caveats. The length is justified by the two-action, seven-parameter surface, and each section helps 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?
An output schema exists, so return values need not be described. Given the 7 parameters, annotations, and two distinct actions, the description covers selection, alternatives, caveats, and examples sufficiently for an agent to call the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 adds usage meaning beyond the schema by explaining action modes, the installed_only+category="lovelace" combination for dashboard card discovery, and the repository_id example; it does not need to restate every parameter.
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?
Names a specific verb and resource (Get HACS info/data) and distinguishes the two operational modes: search/browse vs repository detail. It also names sibling ha_manage_hacs for installation and domain-specific tools for non-HACS work, 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?
Explicitly states when to use action="search" versus action="info" and names the alternative tools for installing/adding repositories or handling non-HACS entities. The dashboard tip and concrete examples further reduce ambiguity about invocation context.
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 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. Administrators can optionally enable recorder workload guardrails in Advanced settings. When enabled, oversized entity/time workloads are rejected before the recorder query is issued; narrow the time range or entity list to stay within the budget. Calendar statistics may first read HA's configured timezone so the estimate follows local calendar boundaries.
EXAMPLES:
ha_get_history(entity_ids="sensor.bedroom_temperature", start_time="24h")
Paginate oldest-first: ha_get_history(entity_ids="sensor.temperature", start_time="7d", limit=100, offset=100, order="asc")
ha_get_history(source="statistics", entity_ids="sensor.living_room_temperature", start_time="6m", period="month", statistic_types=["mean", "min", "max"])
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | 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). | |
| 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 |
| end_time | No | End time: ISO datetime. Default: now | |
| 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 relative (e.g., '24h', '7d', '30d'). 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 declare readOnly/idempotent/non-destructive, yet the description adds substantial behavioral detail: compact-key renaming, ISO timezone semantics, the lc->lu fallback caveat, client-side slicing, the multi-entity offset=0 restriction, and optional recorder guardrails. This is far beyond what 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?
Well-organized with labeled sections (WHEN NOT TO USE, WHEN TO USE, CAVEATS, EXAMPLES) and a one-line purpose up front. The CAVEATS block is dense and some lines verge on internal implementation detail, 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 14-parameter tool with an output schema present, the description supplies the routing, mode selection, pagination, and timezone caveats an agent needs. Return-format details are correctly left to the output schema, and nothing critical 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 real semantics: the multi-entity offset=0 constraint, client-side limit/offset behavior, and worked examples of relative start_time and statistics parameters. It stops short of documenting every parameter but meaningfully extends 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+resource ('Get historical data from Home Assistant's recorder') and immediately differentiates the two data modes. It explicitly names the siblings it is not (ha_get_state, ha_manage_energy_prefs), so an agent can route without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit WHEN NOT TO USE and WHEN TO USE sections, naming the alternative tools and the exact conditions that select them. The source="history" vs source="statistics" guidance ('troubleshoot why a value changed' vs 'long-term trends and period averages') leaves nothing 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_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.
EXAMPLES:
List / search: ha_get_integration(query="zigbee")
Get an entry with its editable fields: ha_get_integration(entry_id="abc123", include_schema=True)
Diagnostics dump, paged along a list-valued path: ha_get_integration(entry_id="abc123", include_diagnostics=True, diagnostics_data_path="", diagnostics_data_limit=10, diagnostics_data_offset=20)
Inspect a subentry reconfigure schema: ha_get_integration(entry_id="abc123", include_subentry_schema=True, subentry_type="conversation", subentry_id="sub123")
List template entries with their options: ha_get_integration(domain="template")
STATES: 'loaded', 'setup_error', 'setup_retry', 'not_loaded', 'failed_unload', 'migration_error'.
OPTIONS: options reflect the entry's persisted values. Values that
match a secrets.yaml entry are returned as "**redacted**". Use
include_schema=True to see every editable field and its
default/type. The shape depends on the read path, which the response
does not name: when the ha_mcp_tools custom component serves the read,
options are the stored mapping as-is, so a field never set may be
absent and a section's fields stay nested under the section key (e.g. a
template helper's additional_options). Otherwise they are read from
the options flow form, which shows unset fields at their schema default
and lists a section's fields at the top level. Check both places for a
section field.
Each entry carries log_level: the canonical Python logger level name
(DEBUG/INFO/WARNING/ERROR/CRITICAL) when the
integration has a log-level override (set one with
ha_set_integration(log_level=...)), or "DEFAULT"
(uppercase sentinel) when no override is set; and log_level_raw: the
original numeric level (e.g. 10 for DEBUG) when HA returned an int,
None otherwise. This is distinct from the app side, where
ha_get_app returns Supervisor's lowercase "default" literal — do
not cross-compare.
| 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 return the options flow schema (available fields and their types). Only applies when supports_options=true. | |
| 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 surfaced here by probing the options flow. Prefer this over include_schema when you only need to read the current values; use include_schema when you also need the field types or selector metadata. | |
| diagnostics_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_knx_project | No | 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. | |
| 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, return introspection-only config subentry schema information; no subentry is created. Pair with subentry_type, and optionally subentry_id for reconfigure schema. | |
| diagnostics_truncate_at_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?
Annotations already establish it as a safe idempotent read, but the description adds substantial context beyond them: secrets.yaml values are returned as '**redacted**', options shape varies by read path (custom component vs. options flow), payload sizes are named (Hue ~290 KB, ZHA/MQTT/ESPHome several MB), and specific warnings for non-KNX entries and diagnostics truncation. The log_level vs. log_level_raw distinction is fully explained.
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 purpose, then EXAMPLES, then STATES, then OPTIONS before the long log_level tail. Structure is clean and every block is load-bearing, but the log_level/log_level_raw paragraph and the cross-comparison aside with ha_get_app are verbose for what is essentially a return-value note that the output schema could carry.
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 21-parameter tool spanning list, detail, diagnostics, schema, and KNX sub-modes, the description covers mode selection, return-shape caveats, redaction, size limits, and pagination. With an output schema present it need not document raw return values, and nothing an agent requires to invoke it correctly appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 each parameter already carries its own description. The prose adds value by giving worked invocation examples that reveal valid multi-parameter combinations and by explaining runtime interactions the schema leaves implicit (e.g., domain filter auto-enabling options, diagnostics_data_path swapping data for a pagination envelope).
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 integration/config entry) and immediately distinguishes its two operating modes: list-all when entry_id is absent vs. detailed retrieval when present. It also contrasts semantics with siblings ha_set_integration (writes log_level) and ha_get_app (logs), so an agent can place 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?
Gives explicit mode selection (with/without entry_id), parameter-combination examples for each major path, and a direct alternative rule: '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.' It also advises pairing diagnostics flags with fields/truncation for large payloads.
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, which structured=False gets back; use it for entries below system_log's WARNING+ ~50-entry cap, or for the per-component rollup. In structured mode limit/order do not apply: issues are ranked by count, then severity, then recency, over a fixed deep window.
Raw-text sources (error_log, supervisor, system_service) read a bounded window per call, so level/search filter and limit slice within that window only; window_lines reports the size requested. Logbook responses carry has_more plus a pagination_hint; error_log and fault_log carry has_more with a next_offset to pass back while it stays true.
| 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, cli, multicast, observer — 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. | |
| limit | No | Max entries/lines to return. 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. 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; 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; 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. | |
| source | No | 'logbook': entity state-change history. 'system': HA's structured system_log entries (errors, warnings). 'error_log': raw log text (home-assistant.log on container/pip installs; HA Core's journald stream on Supervisor-backed installs). 'supervisor': app (add-on) container logs (needs slug). 'system_service': Supervisor-managed system service logs (needs slug). 'logger': effective log level per integration (confirms ha_set_integration(log_level=...) changes took effect). 'fault_log': HA Core's faulthandler crash dump (home-assistant.log.fault), written only when HA dies from a native fatal signal, which never reaches journald or error_log; empty on a healthy install (crash_recorded=False); whole crash blocks are ordered with each block's lines kept in place; reads through the 'HA-MCP File & YAML Tools' entry. | logbook |
| 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). | |
| entity_id | No | Logbook only: restrict to this entity. | |
| 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?
Annotations already cover safety (readOnly, idempotent, non-destructive), yet the description adds substantial behavioral detail: system_log counts run since each error first occurred while error_log counts only the fetched window, structured mode drops tracebacks, counts are ranked by frequency then severity then recency over a fixed deep window, and raw sources read a bounded window. This is well beyond what 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?
Dense but front-loaded: the triage recommendation and the system-vs-error_log tradeoff lead, and the pagination/raw-window caveats follow. Every sentence carries operational information, though the middle paragraph is packed enough that an agent must parse carefully; slightly heavy for the value delivered.
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 described, and the description still supplies the pagination contract (has_more plus next_offset/pagination_hint), the compact-mode truncation caveat, and per-source edge cases like the fault_log empty-on-healthy-install behavior. Nothing needed to call any of the seven sources 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 already 100%, so the baseline is 3, but the description adds cross-parameter semantics the schema cannot express on its own — notably 'In structured mode limit/order do not apply' and how search matches differ (message+logger name in structured mode vs whole line on the raw path). It also clarifies that level is an exact match, not a threshold, and that window_lines reports requested size.
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 Home Assistant logs') and then enumerates the seven distinct sources with precise semantics for each, so an agent knows exactly what data flows back. It does not, however, differentiate itself from sibling tools that overlap conceptually (e.g. ha_get_history for logbook-style state history, or ha_get_system_health), so it stops short of full sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The opening directly prescribes the default: 'Prefer source="system" for triage', then gives explicit when-to-use and when-not guidance ('use it for entries below system_log's WARNING+ ~50-entry cap, or for the per-component rollup'). It also routes between structured=True/structured=False and explains what each source is for, which is effectively explicit alternative-selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_get_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.
| 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. 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. | |
| 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/openWorld, but the description adds meaning beyond them: fields= projection short-circuits unrelated state/service/registry reads, domain counts and states_summary stay complete despite pagination, and settings_url / ha_mcp_update are emitted under specified conditions. It stops short of describing pagination mechanics, but the added context is substantial.
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 not-for list are front-loaded and every paragraph addresses a distinct concern (routing, cost, projection, settings_url, update check). The settings_url and ha_mcp_update paragraphs are verbose for what is essentially output-handling guidance, so it is efficient but not tight.
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, yet the description goes further and still disambiguates sibling choice, cost profile, projection behavior, and conditional output fields. 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 coverage is 100% so the schema documents all 10 params, but the description adds real nuance: it clarifies detail_level defaults, the fields= projection behavior, and that domain counts ignore limit pagination. This is meaning beyond the schema, warranting above the 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+resource ('Get AI-friendly system overview') and enumerates the concrete payload (base_url, version, location, timezone, entity overview, notifications). It also explicitly names the siblings it is not (ha_get_state, ha_get_entity, ha_search), 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?
Gives explicit when-to-use ('Use minimal for most queries') and when-not-to-use ('Do not use this tool to inspect a known entity or a narrow set of entities'), routing to the correct alternatives by name. It further warns that an unprojected overview is expensive on large installations, which is actionable selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_get_skill_guideGet Home Assistant Best Practices Skill 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
outside the projected record(s) — at the response wrapper level in bulk
mode, at the top-level result (sibling of data/metadata) in
single-entity mode — so fields=["state"] still returns a record with
only state.
EXAMPLES:
Single: ha_get_state("light.kitchen")
Multiple: ha_get_state(["light.kitchen", "light.living_room", "sensor.temperature"])
Slim bulk: ha_get_state(["light.kitchen", "sensor.temperature"], fields=["state", "attributes"], attribute_keys=["brightness"])
get current state value single entity check status bulk multiple states 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 already declare read-only/idempotent/non-destructive, and the description goes well beyond them: bulk partial-success semantics ('success=True if at least one state was retrieved'), error_count reporting, fields projection behavior on the wrapper vs record, and the no-op + warnings behavior when attribute_keys is used without 'attributes' in fields. This is exactly the extra behavioral context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The body is dense but front-loaded with examples and every clause about projection behavior earns its place. However, the trailing fragment ('get current state value single entity check status bulk multiple states which lights"') is noisy keyword residue that adds no meaning and slightly undermines 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, the description needn't restate return shapes, and it instead supplies the non-obvious bulk/projection semantics an agent needs. Annotations cover safety, schema covers parameters, description covers behavior — coverage is complete for a 3-param read 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 3 is the baseline, but the description materially adds meaning the schema lacks: that fields projects per-entity record keys rather than the outer bulk wrapper, that attribute_keys only applies when 'attributes' is in fields, and that duplicates are deduplicated with a 100-item cap. This is substantive semantics beyond the 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?
States a specific verb ('Get'), resource ('state, attributes'), and entity scope (single vs list of up to 100). It implicitly distinguishes itself from ha_get_history by emphasizing 'current' state and from write tools like ha_call_service, but it never names a sibling explicitly.
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 explains the two usage modes (string for one entity, list for bulk) and gives worked examples for each. It does not state when to prefer this over alternatives such as ha_get_history or ha_search, nor any exclusion conditions, 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_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 — uid, summary, status (needs_action or completed), description, and due date where supported — optionally filtered by status (None returns all).
EXAMPLES:
List all todo lists: ha_get_todo()
Get incomplete items: ha_get_todo("todo.shopping_list", status="needs_action")
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Filter items by status: 'needs_action' for incomplete, 'completed' for 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 declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so safety is covered. The description goes beyond them by disclosing what is actually returned in each mode (entity_id/friendly_name/state; uid/summary/status/description/due date 'where supported') and the status filter's interaction with entity_id, which is genuine added 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?
Front-loaded with the tool's purpose, then the two modes in parallel structure, then two compact examples. Every sentence carries information; nothing is restated from 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?
Although an output schema exists (so return values need not be described), the description nonetheless covers both branches of behavior, the meaning of the optional filter, and the returned fields, leaving no ambiguity about how to invoke the tool in either 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 description coverage is 100%, so the baseline is 3; the description still adds value by explaining the two parameters as a coupled switch (entity_id selects the mode, status only filters items and 'None returns all') and by showing the pairing in the examples. It does not add format or syntax detail beyond 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 and resource ('get todo lists or items') and immediately splits the two operating modes: domain-wide listing without entity_id vs. item retrieval with entity_id. An agent can distinguish this read tool from the mutation siblings ha_set_todo_item and ha_remove_todo_item 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 clearly defines when each mode applies ('Without an entity_id... With an entity_id...') and the worked examples show concrete call shapes. It stops short of naming alternatives or exclusions (e.g., pointing at ha_set_todo_item for modifications), so it is clear context rather than full routing guidance.
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="abc123")
With the ha_mcp_tools custom component installed, YAML-defined zones —
including the auto-synthesized 'home' zone — are included and marked
editable=false / source="yaml" (storage zones created via UI/API
are source="storage"). Without the component, only storage zones are
listed and YAML-defined zones such as 'home' will not appear.
| Name | Required | Description | Default |
|---|---|---|---|
| zone_id | No | Zone ID to get details for (from 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 the safety profile (readOnly, idempotent, non-destructive, not open-world), and the description adds value beyond them: it explains that YAML-defined zones (including the auto-synthesized 'home' zone) only appear when the ha_mcp_tools custom component is installed, and that such zones are marked editable=false / source="yaml" while UI/API zones are source="storage". That is real behavioral context an agent cannot get 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-loads the two operating modes and examples before the longer footnote about the custom component. The YAML/storage paragraph is dense but each clause carries distinct information, so there is little waste aside from mild verbosity in that final block.
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 described, and the description covers the remaining gaps: the two call modes, a concrete example for each, and the non-obvious condition under which zones go missing. Nothing an agent needs to call this correctly is absent.
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 single zone_id parameter is well documented in the schema itself ('from ha_get_zone() list'). The description's explanation of the optional-parameter mode switch adds a little meaning, but the parameter-level semantics are already carried by 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?
States a specific verb and resource ('Get zone information') and immediately clarifies its dual behavior: list-all when no zone_id is supplied, detailed config when one is. This distinguishes it cleanly from the write-side siblings ha_set_zone and ha_remove_zone, and the read-only nature is 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?
The 'Without a zone_id: ... With a zone_id: ...' structure plus two concrete call examples tells the agent exactly when to use each mode. It stops short of naming alternatives explicitly (e.g. 'use ha_set_zone to modify'), so it is clear context rather than explicit routing, but the mode selection is fully specified.
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. | |
| 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, 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?
Annotations already declare destructive/openWorld/not-idempotent, yet the description adds substantial behavior beyond them: self-update limitation (ha-mcp cannot update its own running slug), shallow merge semantics for options, replacement semantics for network overrides, Ingress-vs-direct-port security tradeoff, and detailed retry/verification guidance for writes with unknown outcomes. That is genuine operational context the annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The prose is front-loaded and each sentence for the first several paragraphs earns its place (modes, prerequisites, retry safety). However, the description ends with a long undifferentiated keyword blob ('manage app apps addon add-on configure settings options port network ...') that adds no selection or invocation value and bloats the definition considerably.
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 23-parameter multi-mode tool this is close to complete: modes, prerequisites, mutual exclusions, failure/retry handling, and environment-specific caveats are all covered, and an output schema exists so return shapes need not be restated. The residual gap is that the mapping from individual config fields to their mode is largely left to the (admittedly rich) schema descriptions.
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 per-field schema labels do not: mode exclusivity ('exactly one mode'), array_patch being mutually exclusive with body/websocket/offset/limit/config params, and the one-level-only merge rule for options and network. It does not restate field-level details, which is appropriate.
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 ('Manage Home Assistant apps (add-ons) or proxy an app API') and immediately distinguishes itself from the sibling ha_get_app by reserving inventory/status reads there. It then enumerates the four mutually exclusive modes, so an agent can tell what class of operation this tool covers 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 routing: call ha_get_app first for inventory/status/Supervisor metadata, use proxy mode only for documented app-specific read APIs, and 'do not infer private app API schemas.' It also states the hard constraint 'Use exactly one mode' with the four alternatives named, and gives prerequisites (HA OS or Supervised).
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 |
|
| Create a full HA tarball (config + addons, no DB by default). Can take a while on a large instance; progress heartbeats are sent while waiting. |
|
| List full HA tarball snapshots (id, name, date, size). Read-only — use to discover a |
|
| Restore a full HA tarball. Restarts HA. Last-resort recovery. |
|
| 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, …) and config subentries require a fresh safety snapshot; 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. A deleted integration config entry cannot be recreated: its snapshot holds only metadata and the enabled state. No HA restart. |
|
| Delete one auto-backup by |
When to use which scope:
Use
scope="edits"to undo a recent automation/script/scene/dashboard/helper edit by the agent. Lightweight, fast, no restart.Use
scope="snapshot"only for system-wide recovery (botched add-on update, mass config corruption, etc.).
scope="snapshot" backup-hint:
Run before operations that CANNOT be undone (e.g., deleting devices). If the current definition was fetched or can be fetched, this tool is usually not needed.
(snapshot, delete) is off by default and layered even when enabled: a human must
set enable_snapshot_delete=true (env var, web settings UI, or add-on Supervisor
options) — an agent cannot turn this on itself. When enabled, a delete call is still
refused if: the target is a scheduled/automatic backup; it's younger than
snapshot_delete_min_age_days (default 7, 0 disables the floor); or it's the single
newest snapshot remaining. These guarantee at least one recovery point always
survives an agent's own mistakes.
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:
Snapshot before risky op:
ha_manage_backup(scope="snapshot", action="create", name="Before_Big_Change")List snapshots (to discover a backup_id or confirm one landed):
ha_manage_backup(scope="snapshot", action="list")Restore full snapshot:
ha_manage_backup(scope="snapshot", action="restore", backup_id="dd7550ed")Delete an old snapshot (requires
enable_snapshot_delete=true):ha_manage_backup(scope="snapshot", action="delete", backup_id="dd7550ed", confirm=True)On-demand entity snapshot before a manual UI edit:
ha_manage_backup(scope="edits", action="create", domain="helper_input_boolean", entity_id="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. | |
| 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. | |
| older_than_days | No | (edits.delete) Bulk-delete auto-backups older than this many days. | |
| restore_database | No | (snapshot.restore) Include the database in the restore; otherwise config only. |
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, but the description goes far beyond that: it discloses that snapshot.restore restarts HA, that snapshot.delete is off by default and layered with age/newest-snapshot guards, that a human must enable toggles an agent cannot flip, how Read Only Mode interacts, and that flow-helper restores get new config-entry IDs. This is unusually rich behavioral disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the scope-first decision with a table, then groups guards, controls, and examples under bold headers, and closes with a runnable example set. It is long, but the complexity (11 params, 12 valid combos) justifies the length; the trailing Claude Desktop timeout paragraph is the one item that sits awkwardly outside selection guidance.
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, the description does not need to explain return values, and it fills every other gap: scope/action routing, guard semantics, human-managed toggles, and recovery edge cases. An agent has everything required to choose a scope and call 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, but the description adds cross-parameter meaning the schema cannot: the full valid (scope, action) matrix, required pairings (domain+entity_id for edits.create, confirm for snapshot.delete), and the distinction between backup_name and backup_id. It stops just short of restating every per-parameter 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?
States a specific verb+resource (manage Home Assistant backups) and immediately decomposes it into two disjoint scopes with a 12-row (scope, action) matrix. An agent can tell exactly what this tool covers and how it differs from config-mutation 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?
Has an explicit 'When to use which scope' section: use `edits` to undo a recent agent edit (fast, no restart) and `snapshot` only for system-wide recovery. It also names the guard conditions for the risky delete path and when a snapshot hint is not needed. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_manage_blueprintsManage 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.
This tool performs writes; to search the store or read repository details use
ha_get_hacs_info. Use action="update_information" to run the HACS UI's
"Update information" action — a forced re-fetch of one repository's release data
from GitHub, so a pending update becomes visible to HACS and its update entity
immediately.
Examples:
Install latest: ha_manage_hacs(action="download", repository_id="441028036")
Install a version: ha_manage_hacs(action="download", repository_id="piitaya/lovelace-mushroom", version="v4.0.0")
Remove: ha_manage_hacs(action="remove", repository_id="owner/repo")
Add a custom repo: ha_manage_hacs(action="add_repository", repository="owner/repo", category="lovelace")
Refresh release data: ha_manage_hacs(action="update_information", repository_id="owner/repo")
Caveats: Installing an integration usually needs a Home Assistant restart to
activate; new Lovelace cards need a browser cache clear. add_repository requires owner/repo
format plus a matching category. Removing an integration deletes its files but
the loaded module persists until the next Home Assistant restart — delete its config
entries first (ha_remove_helpers_integrations). HACS refreshes custom
repositories on its own only about every 48 hours, so update_information is the
way to surface a just-published release.
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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, or 'update_information' to refresh a repository's release data from GitHub | |
| version | No | Specific version to install (action='download') | |
| category | No | Repository category (action='add_repository') | |
| 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') |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Although annotations already declare destructiveHint=true, non-idempotent, and open-world, the description adds substantial behavioral context: installs usually need an HA restart, Lovelace cards need a browser cache clear, remove deletes files but the loaded module persists until restart (delete config entries first), and HACS only self-refreshes about every 48 hours. This is rich, non-redundant disclosure 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?
It is front-loaded (purpose, then routing, then examples, then caveats) and every section is scannable. The Claude Desktop timeout workaround is somewhat tangential and verbose, but it is genuinely useful 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?
For a multi-action mutation tool with an output schema, the description covers action semantics, prerequisites, post-action side effects, and refresh behavior. Nothing an agent needs in order to invoke it correctly is missing, and return values are handled by 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 schema already documents every parameter and the enum. The description still adds value with concrete worked examples showing which parameter combinations correspond to each action, plus the constraint that add_repository requires 'owner/repo' format with a matching category. That is above the baseline 3 but not a substitute for 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 names a specific resource (HACS) and enumerates the concrete operations (install/update, remove, add custom repositories, refresh info), and explicitly distinguishes itself from the read-only sibling ha_get_hacs_info. An agent can tell it apart from sibling 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?
It states 'This tool performs writes; to search the store or read repository details use ha_get_hacs_info', giving an explicit alternative and the condition that selects it. Per-action guidance for update_information versus add_repository versus remove further routes the agent correctly.
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 (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 already declare destructiveHint=true and idempotentHint=true, but the description adds substantial context beyond them: 'set' changes only the backend default while users with an explicit profile choice keep theirs, theme names are validated at call time, the screenshot engine silently writes the token owner's saved theme (flipping their live sessions), and the expected_current guard is best-effort/non-atomic. This is far beyond what 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?
Well front-loaded and organized with headers (When NOT to use, When to use, screenshot-engine actions, Caveats, Examples), and every section earns its place for a tool with cross-tool side effects. It is long, and the trailing Claude Desktop timeout workaround, while operationally useful, is tangential to describing the tool itself.
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 6 parameters, a mutation surface, and cross-tool side effects, the description covers purpose, alternatives, per-action semantics, caveats, and worked examples. Return values need not be explained since an output schema exists, so nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 practical semantics the schema does not: quoting 'BOTH values' from the warning into set_engine_theme, examples showing value={'theme':'','dark':False}, mode defaulting to light, and the guard/force interaction. This meaningfully extends parameter understanding beyond the structured fields.
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 frontend themes') and then breaks the tool down by action ('list' discovers installed theme names, 'set' selects the backend default). It explicitly distinguishes itself from siblings that touch related layer (themes as YAML files, HACS install, per-user engine theme), so an agent can tell it apart without reading 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 explicit 'When NOT to use' and 'When to use' sections naming alternatives: installing themes goes through ha_manage_hacs, editing theme files goes through ha_config_set_yaml. It also distinguishes backend-default ('set') from per-user engine profile ('set_engine_theme'/'get_engine_theme'), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_manage_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.
Installs run asynchronously in Home Assistant and can take minutes: 'install' returns once the service calls are accepted, with per-entity results. Poll action='list' to watch in_progress until installed_version reaches latest_version. action='list' also returns ha_mcp_update — this MCP server's own update status {current, latest, update_available}, so a newer ha-mcp release can be flagged.
EXAMPLES:
Pre-update analysis: ha_manage_updates(action="get", entity_ids=["update.home_assistant_core_update"], include_release_notes=True)
Update everything pending in a category: ha_manage_updates(action="install", categories=["addons", "hacs"])
Dismiss a repair: ha_manage_updates(action="ignore_repair", repairs=[{"domain": "sun", "issue_id": "abc"}])
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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?
Beyond the annotations (destructiveHint=true, idempotentHint=false, openWorldHint=true), the description discloses genuinely non-obvious behavior: installs run asynchronously and return before completion, per-entity results are returned, polling action='list' is the way to track progress, and ha_mcp_update is surfaced in list output. The Read Only Mode gating and the Claude Desktop timeout workaround are additional operational context the annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and behavior are front-loaded and the examples are useful, but the prose is long and the trailing keyword-stuffing list ('update updates install skip firmware core os repair repairs issue ignore dismiss unignore') is pure noise. The extended Claude Desktop timeout paragraph, while helpful, is disproportionate to the call itself.
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, yet the description still covers the essentials an agent needs: async install semantics, how to verify completion, Read Only Mode gating, and where repairs originate. Nothing material for correct invocation 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 every parameter including the categories exclusion rule and the entity_ids/categories mutual exclusivity. The description reinforces usage via examples but adds little syntax or meaning beyond what the schema provides, 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?
The opening sentence names specific verbs (list, details, install, skip, ignore) against a specific resource (Home Assistant updates and Repairs issues), so the tool's scope is unambiguous. It further disambiguates from the sibling ha_get_overview by stating that Repairs are listed there, not 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?
It gives explicit action-by-action routing, names the alternative tool for listing repairs, states the Read Only Mode behavior (read actions stay, writes blocked), and provides three concrete examples showing which action/parameters to combine. There is little left to infer.
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 or integration is still active, the entity may be re-added automatically on the next HA restart or reload. This cannot be undone without restoring from backup. For most use cases, consider disabling instead: ha_set_entity(entity_id="sensor.old", enabled=False).
BULK MODE: Pass a list of entity IDs to remove up to 100 at once — handy for clearing the restored=true orphans an integration leaves behind after its filters change. Removals run sequentially and return: {removed: [...], skipped: [...], errors: [{entity_id, code, message}]} where skipped = ids already absent (not-found is idempotent, not an error). Bulk mode is NOT auto-backed-up (the snapshot is single-entity); single-id removal still is.
EXAMPLES:
Remove orphaned sensor: ha_remove_entity("sensor.old_temperature")
Bulk cleanup: ha_remove_entity(["sensor.orphan_1", "sensor.orphan_2"])
Use ha_search / ha_get_entity to verify an entity before removing it.
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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?
Goes well beyond the annotations (destructiveHint/idempotentHint), disclosing that removal is permanent and unrestorable without a backup, that re-addition can occur automatically, that bulk mode is limited to 100 IDs and is NOT auto-backed-up while single-id removal is, that removals run sequentially, and that not-found ids are returned as skipped rather than errors. The backup asymmetry between single and bulk mode is exactly the kind of trait 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-loaded with purpose and the critical irreversibility warning, then cleanly sectioned into BULK MODE and EXAMPLES. It is on the long side and the closing Claude Desktop timeout workaround paragraph is verbose and tangential to invocation, though it is operationally useful.
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?
Despite an output schema existing, the description still documents the return shape (removed/skipped/errors) because the bulk response semantics are non-obvious. Combined with the alternative path, verification advice, and idempotency semantics, an agent has everything needed to call this safely and interpret the result.
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 a single well-documented parameter, so the baseline is 3; the description adds real meaning on top, specifying that the list form triggers bulk mode, caps at 100, runs sequentially, and reports skipped/errors. It does not, however, add any per-value formatting or validation rules 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 (remove) and resource (entities in the Home Assistant entity registry), and explicitly scopes it to the registry rather than the device/integration, which separates it from siblings like ha_remove_device and ha_remove_helpers_integrations. An agent can identify the operation and its target 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 explicit when-to-use (orphaned or stale entity entries), when-not-to-use (if the underlying device/integration is still active the entity will be re-added on restart), and names a concrete alternative with call syntax: ha_set_entity(entity_id="sensor.old", enabled=False). It also tells the agent to verify first via ha_search / 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_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.
ROUTING:
SIMPLE helper_type (input_button, input_boolean, input_select, input_number, input_text, input_datetime, counter, timer, schedule, zone, person, tag) + bare helper_id or entity_id → websocket delete.
FLOW helper_type (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 (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, 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 destructive/idempotent, but the description goes far beyond them: it discloses cascade behavior (sub-entities removed together), structured error codes per failure mode, retry-branchable transient codes, and backup/restore limitations via `ha_manage_backup`. The idempotency claim ('Calling N times gives the same response') and the refusal path (VALIDATION_INVALID_PARAMETER, nothing deleted) are exactly the kind of 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?
Front-loaded with purpose, then cleanly sectioned into WHEN NOT TO USE, ROUTING, errors, examples, and warnings. It is long, and the closing Claude Desktop timeout workaround is tangential, but nearly every line carries actionable routing or failure 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 destructive, multi-path removal tool with five parameters and an output schema, the description supplies routing, failure codes, cascade effects, and recovery guidance. Nothing material for correct invocation is missing, and return-value details are 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. The description earns above baseline by explaining what each parameter value *routes to* — helper_type selects websocket vs config-entry vs subentry deletion, entity_id resolution goes through the entity registry, and the confirm flag's blocking role is shown in every example.
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') and immediately differentiates itself from the closest sibling by naming `ha_remove_entity`. The routing section makes clear it unifies three distinct backend removal mechanisms, so an agent understands the tool's scope precisely.
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?
An explicit WHEN NOT TO USE section names two exclusions (entity-only removal, YAML-configured helpers) and routes each to the correct alternative, including `ha_remove_entity` and direct YAML editing. The ROUTING block gives deterministic when-to-use rules keyed off helper_type/entity_id/entry_id combinations.
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 ItemADestructiveIdempotent
Remove an item from a Home Assistant todo list.
Permanently deletes an item from the specified todo list. To mark it completed instead, use ha_set_todo_item() with status="completed".
EXAMPLE: ha_remove_todo_item("todo.shopping_list", "Buy milk")
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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 | Yes | Item to remove - can be the item UID (from ha_get_todo) or the exact item summary/name | |
| entity_id | Yes | 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 declare destructiveHint=true, readOnlyHint=false, and idempotentHint=true. The description adds that deletion is permanent and provides a Claude Desktop timeout workaround, but does not cover auth requirements or broader failure modes.
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, permanence warning, alternative routing, and example are front-loaded and efficient. The final paragraph about Claude Desktop timeout behavior is niche and lengthy, but it is clearly separated and potentially useful for that client.
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, full schema coverage, and an output schema, the description provides everything needed to select and invoke the tool correctly. It explains the effect, the alternative, and an example, while return values are handled by 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 description coverage is 100%, so both entity_id and item are already documented. The description adds an example call, but the schema itself explains that item may be a UID or exact summary, so the description does not add substantial meaning beyond the structured data.
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: 'Remove an item from a Home Assistant todo list,' and clarifies it permanently deletes from the specified list. It distinguishes itself from ha_set_todo_item() by naming the alternative explicitly.
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 directly tells the agent when to use this tool versus the sibling alternative: to mark an item completed, use ha_set_todo_item() with status="completed". This is an explicit when/when-not guideline for the primary alternative.
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("abc123")
WARNING: Removing a zone used in automations may cause those automations to fail. Use ha_get_zone() to find the zone_id for the zone you want to remove.
NOTE: The 'home' zone cannot be removed as it is typically defined in configuration.yaml.
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, as clicking it too quickly is known to cause hangs in Claude Desktop. This workaround is specific to Claude Desktop.
| Name | Required | Description | Default |
|---|---|---|---|
| zone_id | Yes | Zone ID to remove |
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=true, idempotent=true, openWorld=false, so safety is partly covered; the description adds genuinely new behavioral context: side effects on automations, the immovable 'home' zone, and an observed timeout/hang failure mode with retry and approval-timing guidance. This is well beyond what the structured fields provide.
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 scannable, but the closing Claude Desktop paragraph is long, capitalized, and tangential to selecting or invoking the tool. It dilutes 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 and annotations cover destructiveness, so the description only needs to add operational context it actually delivers: prerequisites, exclusions, side effects, and failure modes. Nothing essential for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description goes slightly further by showing the identifier format via the example call ha_remove_zone("abc123") and telling the agent where to obtain the value (ha_get_zone()). It does not explain what happens if the ID is invalid or not found.
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"), so an agent immediately knows the operation. However, it never explicitly distinguishes itself from adjacent removal siblings (ha_remove_area_or_floor, ha_remove_entity, ha_remove_device), leaving that separation to be inferred from the name.
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 (use ha_get_zone() to find the zone_id), an explicit exclusion (the 'home' zone cannot be removed because it lives in configuration.yaml), and a consequence warning that automations referencing the zone may fail. That covers when to use it, when not to, and the risk profile.
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 the report is about a missing tool: a
stale client tool 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?
Adds substantial context beyond the readOnly/idempotent annotations: response size and the fields= narrowing lever, secret redaction, the GitHub URL-length truncation of log sections, and the placeholder behavior when no text is passed. It does not, however, say anything about rate limits or whether repeated calls are cached, so it stops short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded purpose then progressively operational detail, and every paragraph carries actionable content. It is longer than strictly necessary — the quoted clarifying question and the URL-limit aside could be trimmed — keeping it just 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?
For a 10-parameter, zero-required tool with an output schema, the description covers the missing pieces: it defers return-value detail to the schema, points to the `instructions` output key for the mandatory anonymisation/duplicate-check workflow, and tells the agent not to surface raw output to the user.
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 still adds value by explaining how the free-text input is consumed ('Pass the report text in the call'), what fields= narrows, and by framing report_type as a judgment call rather than a raw enum.
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 ('Get diagnostics and a finished GitHub issue') and enumerates the three supported report kinds. It is instantly distinguishable from every sibling ha_* tool, which are Home Assistant operations rather than a self-reporting/diagnostics tool.
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 it before filing any GitHub issue about ha-mcp') with a concrete consequence for skipping it, plus a decision prompt for the ambiguous report_type case and an ordered pre-check ('Check missing_tool_hint FIRST when the report is about a missing tool').
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.
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. |
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, but the description adds substantial context beyond them: 1-5 minute downtime, all automations unavailable, automatic config validation before proceeding, and the embedded-mode reply-before-restart behavior. It even documents the Claude Desktop timeout/approval workaround, which is operationally critical and unavailable elsewhere.
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 WARNING and restart consequences, then alternatives, then the example — good ordering. The Claude Desktop workaround paragraph is lengthy and narrow, adding some bulk, though it is genuinely actionable for the 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?
A mutation tool that could leave the server unreachable is fully covered: cost, duration, validation, embedded behavior, and the alternative path. An output schema exists, so return values need no elaboration.
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 that confirm must be True, so the baseline is 3. The description adds a concrete invocation example (ha_restart(confirm=True)) that reinforces correct usage, giving a small 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 ("Execute a Home Assistant restart") and immediately distinguishes itself from siblings by naming ha_reload_core and ha_get_system_health. An agent knows exactly what this does versus the reload alternative.
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 (full restart) and when-not (config changes → ha_reload_core; pre-check → ha_get_system_health(include="config_check")). Alternatives and their selecting conditions are spelled out, not implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ha_searchSearchARead-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.
Two surfaces run in parallel and return tagged results:
entities: entity-registry matches (entity_id, friendly name, area). Filter with
domain_filter/area_filter/state_filter.automations / scripts / scenes / helpers / dashboards: matches inside config definitions — triggers, actions, sequences, scene entity-sets, helper bodies, dashboard cards. Driven by
query; narrow withsearch_types.
Use dedicated get/list tools first for a known resource type, including ha_config_get_scene for scene listing and content search. Use this for broader discovery or deep searches across resource types.
For control requests with exclusions such as "except", "excluding", or
"but not", include is_group and member_entity_ids in result_fields.
Do not control an aggregate whose members include an excluded entity;
prefer leaf entities when the exception cannot be verified safely.
A withheld member list still returns is_group=true; absence of
member_entity_ids must not be interpreted as a leaf entity.
When NOT to use:
To read a known entity_id's state: use
ha_get_state(cheaper).To inspect one automation/script/scene config by id: use the matching
ha_config_get_*.To list installed Apps (add-ons): use
ha_get_app.
Config-body search is skipped when domain_filter/area_filter/
state_filter signal entity-only intent (keeping name lookups off the
expensive backend); a warnings[] entry names the skip. Repeat without
entity filters to search configuration contents too.
Caveats:
partial: Truemeans results are NOT exhaustive — a surface raised, or the config-body branch lost data (per-id time budget exhausted, an individual fetch failed, or a helper-type list fetch failed). Empty buckets withpartial: Truemean "search failed", not "no results". The cause is inpartial_reason, also mirrored intowarnings[]with an "incomplete results: " prefix. Do not treat a partial response as complete.countis items in this response (post-pagination), not corpus totals — useentity_total_matches+config_total_matches.limit/offsetapply per-surface. Flathas_more/next_offsetpage the next call (iterateoffset = next_offset); per-surfaceentity_*/config_*variants show which surface still has results.
Examples: - Find a light by name: ha_search("kitchen", domain_filter="light") - List sensors in an area: ha_search(domain_filter="sensor", area_filter="Living Room") - Find lights safely before an "all except one" control request: ha_search("living room", domain_filter="light", result_fields=["entity_id", "friendly_name", "is_group", "member_entity_ids"]) - Which automations use an entity: ha_search("light.bed_light") - All unavailable entities: ha_search(state_filter="unavailable")
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 only declare the safety profile (readOnly, idempotent, non-destructive, closed-world), yet the description discloses far more: partial-result semantics with partial_reason/warnings, that empty buckets under partial mean failure, count vs. corpus totals, per-surface limit/offset and has_more/next_offset iteration, and silent config-body skipping. This is unusually rich behavioral disclosure beyond structured data.
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 purpose, then surfaces, then exclusions, then caveats, then examples — good ordering. It is long and contains some overlap with the schema (e.g., the config-body-skip rule is stated in both), and the aggregate-control paragraph is tangential to searching, but the length is largely justified by 15 parameters and two surfaces.
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 need not be explained, and the description still covers pagination, partiality, filtering interactions, and alternatives. Nothing an agent needs to select or invoke this tool 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, but the description adds cross-parameter semantics the schema does not: result_fields must include is_group/member_entity_ids for exclusion control requests, an exact entity_id query reports referrers even when configs are unreadable, and entity filters signal entity-only intent that suppresses body search. These interactions are genuinely useful; it falls short of 5 only because per-parameter meanings largely restate 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 ('Search for entities ... AND inside automation/script/scene/helper/dashboard configurations') and explicitly enumerates the two parallel surfaces. It distinguishes itself from siblings by naming ha_get_state, ha_config_get_*, ha_get_app, and ha_config_get_scene as the alternatives for known 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?
Has an explicit 'When NOT to use' block routing each case to a specific sibling, plus a positive statement of when this tool is preferred ('broader discovery or deep searches across resource types'). The exclusion-control guidance and the entity-filter-skips-config-body note give concrete usage conditions.
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(identifier="automation.xxx", enabled=False) (or enabled=True to re-enable). For scripts, ha_config_set_script(script_id="script.xxx", run="stop") only stops a currently running execution; it does not disable the script. Home Assistant has no script runtime enable/disable service.
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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 declare destructiveHint=true and idempotentHint=true, and the description adds substantial non-obvious behavior: renames do not propagate to automations/scripts/templates/dashboards, three distinct rename limitations, that disabled entities vanish from state queries until integration reload, and that enabled=False is blocked for automations/scripts. These are exactly the side effects an agent needs before a destructive mutation.
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?
It is long, but the length is justified by 17 parameters and multiple destructive edge cases, and it is cleanly front-loaded with the core purpose followed by headed sections. The Claude Desktop timeout workaround is somewhat tangential and could be trimmed, but nothing else is 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?
Given the complexity and the presence of an output schema (so return values need not be described), the description covers everything an agent needs: bulk constraints, rename semantics, dependency implications, disable semantics, and tool alternatives. 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 the baseline is 3, but the description adds real value beyond the schema by documenting cross-parameter constraints not expressible in the schema (bulk mode supports only labels/expose_to/categories; options domains are paired one at a time; use_device_name is not combinable with name). The worked examples further clarify how parameters combine.
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 then enumerates the full set of modifiable fields, so the agent knows exactly what this tool covers. It is clearly differentiated from siblings by routing related operations to ha_set_device, ha_config_set_automation, and ha_config_set_script.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use it and when not to: the BULK OPERATIONS section lists which parameters are valid when entity_id is a list, the ENABLED/DISABLED section names the alternative tools for automations and scripts, and it points to ha_search/ha_get_device and ha_config_get_label for lookups. This is about as complete as routing guidance gets.
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, or reconfigure.
Modes (pick one):
Enable/disable: entry_id + enabled.
Add integration: domain (+ config) — drives the domain's config flow, including menus and multi-step forms.
Update options: entry_id + config — drives the entry's options flow (what the "Configure" button does in the HA UI).
Reconfigure: entry_id + reconfigure=True + config — connection settings such as host, port, credentials. Repeat with the token the preflight returns as confirm_token to apply.
Log level: log_level + domain (or entry_id) — sets how much the integration logs; read it back with ha_get_integration's log_level.
WHEN NOT TO USE:
Helpers (template, group, utility_meter, ...): use ha_config_set_helper.
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').
Removing an entry: use ha_remove_helpers_integrations.
Use ha_get_integration() to find entry IDs, and ha_get_integration(entry_id=..., include_schema=True) to inspect the options fields before an update. Its supports_reconfigure field tells you whether an entry qualifies for reconfigure=True.
Caveats: adding an integration runs its config flow exactly as the HA UI would (may pair devices, scan the network, create entities). Flows requiring a browser step (OAuth) or an asynchronous provider step error out at that step instead of completing. Reconfigure edits the settings a live integration connects with: a wrong host or credential takes it offline, and there is no automatic rollback — the returned rollback metadata describes repeating the official flow by hand with the previous values, which this tool cannot read back. The preflight does not validate config keys against the integration's form; wrong field names surface on the confirm call.
EXAMPLES:
Add: ha_set_integration(domain="workday", config={"name": "Workday"})
Update options: ha_set_integration(entry_id="abc123", config={"scan_interval": 30})
Reconfigure preflight: ha_set_integration(entry_id="abc123", reconfigure=True, config={"host": "10.0.0.5"}), then repeat adding confirm_token="sha256:..."
Debug logging: ha_set_integration(domain="zha", log_level="DEBUG"), then log_level="DEFAULT" to stop
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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 |
|---|---|---|---|
| config | No | 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. | |
| 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'. | |
| 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 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. | |
| reconfigure | No | 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. | |
| expected_mac | No | Requires reconfigure=True. MAC or IEEE the entry's device must still report. | |
| confirm_token | No | 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. | |
| expected_device_id | No | Requires reconfigure=True. Device registry ID the entry must still own, before and after the change. | |
| 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 already declare destructiveHint=true and idempotentHint=false; the description goes well beyond them by disclosing that adding runs the real config flow (pairing, network scans), that browser/OAuth or async provider steps error out, that a bad reconfigure host/credential takes the integration offline with no automatic rollback, and that log-level changes persist only until restart. These are exactly the side-effect details 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-loaded with the purpose and a clear Modes/Caveats/Examples structure, so the length is largely earned. It loses a point for the misplaced Claude Desktop timeout workaround paragraph and a trailing keyword dump ('integration config entry enable disable...') that add bulk 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 an 11-parameter, multi-mode, destructive mutation tool, the description covers every mode's prerequisites, mutation risks, the preflight/confirm cycle, prerequisites for the expected_* guard params, and where to verify state. An output schema exists, so return-value detail 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 real meaning by mapping parameter combinations to modes (entry_id+enabled, domain+config, reconfigure+confirm_token) and showing worked examples. It does not restate the finer schema text about patch semantics or step_values, so it complements rather than duplicates.
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 an integration (config entry)') and enumerates the five modes it covers, so an agent knows exactly what actions are dispatchable. It distinguishes itself from ha_config_set_helper, ha_remove_helpers_integrations and ha_get_integration by name, which is unusually strong sibling separation.
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 a dedicated WHEN NOT TO USE block that routes helpers, config subentries, the 'otp' helper, and entry removal to their correct siblings, plus how to obtain entry IDs and how to inspect options fields before an update. The mode list itself is a when-to-use map keyed to parameter combinations.
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 or update a todo item in Home Assistant.
Without item (create mode): creates a new item; summary is required.
With item (update mode): updates that item; at least one of rename,
status, description, due_date or due_datetime is required.
EXAMPLES:
Add item: ha_set_todo_item("todo.tasks", summary="Pay bills", due_date="2024-12-31")
Complete item: ha_set_todo_item("todo.shopping_list", item="Buy milk", status="completed")
Rename item: ha_set_todo_item("todo.tasks", item="Old task", rename="New task name")
Not all todo integrations support all features: the Shopping List integration supports summary and status but not descriptions or due dates.
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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. Only used in update mode. | |
| status | No | Item status: 'completed' to mark done, 'needs_action' to mark incomplete. Only used in update mode. | |
| 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') | |
| entity_id | Yes | Todo list entity ID (e.g., 'todo.shopping_list') | |
| description | No | Detailed description for the item | |
| due_datetime | No | Due datetime in ISO format (e.g., '2024-12-25T14:00:00'). Overrides due_date if both provided. |
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=false, destructiveHint=true and idempotentHint=false, so the mutation profile is covered. The description adds value beyond that with mode semantics, integration-dependent feature support, and a concrete Claude Desktop timeout/hang workaround — though it never explains why the tool is flagged destructive or whether updates are reversible.
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 mode distinction, then examples, then integration caveats, then the Desktop workaround — a sensible priority order. Slightly penalized because the final Claude Desktop timeout paragraph is long and environment-specific, 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 an output schema present, return values need no explanation. For an 8-parameter mutation tool with 100% schema coverage, the description supplies the remaining decision-relevant context: mode selection, per-mode requirements, integration limitations, and worked examples.
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 exceeds that by documenting cross-parameter constraints the schema cannot express: summary is required in create mode and ignored in update mode, and update mode needs at least one of five specific fields. The worked examples further anchor argument usage.
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 or update a todo item in Home Assistant') and immediately splits the operation into two named modes (create vs update), which lets an agent distinguish it from siblings like ha_get_todo and ha_remove_todo_item 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 states the trigger condition for each mode: without `item` it creates (summary required), with `item` it updates (one of rename/status/description/due_date/due_datetime required). It also adds a real exclusion — the Shopping List integration does not support descriptions or due dates — so the agent knows when a feature will silently not apply.
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). The 'home' zone is typically defined in YAML and cannot be modified here.
EXAMPLES:
Create: ha_set_zone(name="Office", latitude=40.7128, longitude=-74.0060, radius=150, icon="mdi:briefcase")
Update: ha_set_zone(zone_id="abc123", radius=200)
If a Claude Desktop user gets a 4-minute timeout with no result on this call, read the target back before repeating it, and tell them to wait a few seconds before clicking the manual-approve button, 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', 'mdi:school') | |
| name | No | Display name for the zone | |
| radius | No | Radius of the zone in meters (must be > 0, defaults to 100 on create) | |
| passive | No | Passive mode - if True, zone will not trigger enter/exit automations (defaults to False on create) | |
| zone_id | No | Zone ID to update (use ha_get_zone to find IDs) | |
| 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 flag this as a destructive, non-idempotent write, and the description adds real operational context beyond them: partial-update semantics ('only specified fields change'), create-vs-update field requirements, the immutability of the 'home' zone, and a concrete Claude Desktop 4-minute timeout/hang workaround. That last item is the kind of environment-specific behavior an agent could not infer from annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then mode rules, then two compact examples, then the environment workaround. Every paragraph earns its place, though the Claude Desktop caveat paragraph is long relative to the core instruction and could be tightened.
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 both modes, create-time requirements, defaults (radius/passive documented in schema), and the special-case zone. An output schema exists, so return values need no explanation. Missing only failure behavior (e.g., duplicate name, invalid coordinates), which is a modest gap for a create-or-update 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, but the description adds meaning the schema cannot express: the schema declares ZERO required parameters, while the description tells the agent that name, latitude and longitude are required on create. It also pairs the update path with concrete example payloads (radius=200) showing partial updates are legal.
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 pair and resource ('Create or update a Home Assistant zone') and immediately clarifies the two operating modes. It also names the sibling it is not ('The home zone ... cannot be modified here'), letting an agent distinguish it from ha_get_zone/ha_remove_zone 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 clear conditional routing: omit zone_id to create (with which fields become required), supply zone_id to update with partial-field semantics. It also states an exclusion (the 'home' zone is YAML-defined). It stops short of naming the concrete alternatives (ha_get_zone for lookup, ha_remove_zone for deletion) in the description body itself.
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.
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
The tool set is large and has several naturally overlapping surfaces (ha_search vs ha_get_state vs ha_get_entity vs ha_get_overview for reading, and ha_config_set_group vs ha_config_set_helper(helper_type='group')), but every description carries explicit 'WHEN NOT TO USE' sections that steer the agent to the correct tool. ha_call_service and ha_bulk_control are also carefully distinguished from the dedicated tools. A few read tools still require careful reading to pick correctly.
All names use the ha_ prefix and a verb_noun shape, but the verb/infix conventions are mixed: some resources use ha_config_get/set/remove (automation, script, scene, label, category), others use bare ha_get/set/remove (entity, device, zone, todo), and many use ha_manage (app, pipeline, blueprints, hacs, theme, radio, backup, updates). The inconsistency is readable but not predictable.
77 tools is far above the 25-tool threshold for 'too many' and makes selection cognitively heavy. The breadth (entities, devices, areas, automations, scripts, scenes, dashboards, helpers, integrations, HACS, apps, backups, radios, energy, calendar, etc.) partly justifies the count, but consolidation into more action-parameterized tools would reduce it substantially.
Coverage of the Home Assistant domain is exceptionally broad, with full CRUD/lifecycle for automations, scripts, scenes, helpers, dashboards, entities, devices, labels, categories, zones, todos, and calendars, plus diagnostics, history, traces, backups and updates. The one notable gap is that ha_get_skill_guide — which many write tools mark as a mandatory first call — is currently broken, creating a dead end in the documented workflow.
Maintenance
Related MCP Connectors
- ZapierOAuthcom.zapier
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 gradedqualityDmaintenanceAn 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.636MIT
- 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.6696 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.988 npmMIT