| ha_get_addonA | Get Home Assistant add-ons - list installed, available, or get details for one. This tool retrieves add-on information based on the parameters: slug provided: Returns detailed info for a single add-on (ingress, ports, options, state) source='installed' (default): Lists currently installed add-ons source='available': Lists add-ons available in the add-on store
Note: This tool only works with Home Assistant OS or Supervised installations. SINGLE ADD-ON (slug provided):
Returns comprehensive details including ingress entry, ports, options, state,
and (when the add-on exposes one) a top-level log_level reflecting the
current Supervisor option — useful for confirming ha_manage_addon log_level changes.
Useful for discovering what APIs an add-on exposes before calling ha_manage_addon. INSTALLED ADD-ONS (source='installed'):
Returns add-ons with version, state (started/stopped), and update availability. AVAILABLE ADD-ONS (source='available'):
Returns add-ons from official and custom repositories that can be installed. repository: Filter by repository slug (e.g., 'core', 'community') query: Search by name or description (case-insensitive)
Example Usage: List installed add-ons: ha_get_addon() Get Node-RED details: ha_get_addon(slug="_nodered") List with resource usage: ha_get_addon(include_stats=True) List available add-ons: ha_get_addon(source="available") Search for MQTT: ha_get_addon(source="available", query="mqtt")
|
| ha_manage_addonA | Manage a Home Assistant add-on — update its configuration or call its internal API. Five mutually exclusive operating modes: Lifecycle mode (when action is one of install/uninstall/start/
stop/restart/rebuild/update):
Runs a Supervisor add-on action on slug. install / update go
through the store (the add-on's repository must be registered — it shows
up in ha_get_addon(source="available")); the rest act on an installed
add-on. This is how an assistant brings an add-on online for the user
(e.g. installing + starting the dashboard screenshot engine). Store-repository mode (when action is add_repository or
remove_repository):
Registers or unregisters a custom add-on store repository. These actions
operate on the store rather than an installed add-on, so they take the
repository param and no slug: add_repository POSTs the
repository URL to /store/repositories; remove_repository DELETEs
/store/repositories/{slug} by the repository's slug. Adding a
repository (e.g. balloob's add-ons) is the missing step that lets an
assistant then install an add-on from it via action="install". Config mode (when any of options/network/boot/auto_update/watchdog is provided):
Updates the add-on's Supervisor configuration via POST /addons/{slug}/options.
All config parameters are optional; only provided fields are updated — current values
are fetched and merged automatically (including one level of nested dicts). Proxy mode (when path is provided without array_patch):
Routes HTTP or WebSocket requests through Home Assistant's Ingress
proxy by default (works on HAOS, Supervised, and off-host PyPI/uvx
installs). Pass port=... to bypass Ingress and connect directly to
an add-on's container port — that mode requires the MCP host to
share Home Assistant's container network (i.e. only the HAOS addon).
Use ha_get_addon(slug="...") to discover available ports and endpoints. ESPHome Device Builder dashboard (current rewrite): config and log
access is a WebSocket JSON-command API, NOT REST. The legacy endpoints
are gone — GET /edit?configuration= now returns the dashboard SPA, and
the old /compile /validate /logs WebSocket paths (which took
{"type": "spawn", ...} bodies) reject the upgrade (HTTP 200). Use
instead: HTTP GET /devices → JSON list of configured devices; each entry's
configuration field is the YAML filename to pass below. WebSocket path="/ws" with body
{"command": "<cmd>", "message_id": "1", "args": {...}}. The server
sends a server_info message first, then one reply per message_id.
Wire-confirmed commands: devices/get_config {configuration} → raw
YAML (in the reply's result); devices/logs (stream)
{configuration, port: "OTA"} → live device logs. Also exposed by the
dashboard frontend (command/arg names not wire-tested here):
devices/update_config {configuration, content} → save,
devices/validate, firmware/compile. The /ws channel stays open, so for a one-shot read or a bounded log
capture pass wait_for_close=False with message_limit (and
message_offset to skip the server_info / config-banner preamble).
Reach the dashboard through Ingress — omit port; direct port= does
not route to it.
Array-patch mode (when path AND array_patch are provided):
Atomic "GET array, mutate, POST array" workflow for addon APIs whose write
contract is "send the whole resource collection back". Operations are applied
in order to a working copy; if any op fails validation (unknown id, collision,
malformed shape) nothing is posted. Returns a compact summary instead of the
full array. Designed for Node-RED /flows and similar endpoints. Response shaping (proxy mode): WebSocket streams can be noisy (e.g. the ESPHome dashboard's devices/logs
dumps the device's full config banner on connect). By default, summarize=True collapses long runs of
non-signal messages into short elision markers; INFO/WARNING/ERROR/exit
lines always pass through. Pagination via message_offset / message_limit
works on the raw collected list before summarize runs. python_transform applies a sandboxed Python expression as a final
post-processing step in both HTTP and WebSocket modes. The variable
response is bound to:
WebSocket: list[dict | str] — parsed JSON messages are dicts,
undecodable frames stay as ANSI-stripped strings. Elision markers
appear as {"elided": N, "note": "..."} dicts when summarize ran. HTTP: dict | list | str — whichever the content-type produced.
Transforms may mutate in place (response.append(...), del response[k])
or reassign (response = [...]). This is post-processing only — it does
NOT provide optimistic-locking or write-back semantics.
WARNING: Setting boot="auto"/"manual" will fail for add-ons whose Supervisor
metadata locks the boot mode. The Supervisor returns an error in this case. NOTE: This tool only works with Home Assistant OS or Supervised installations. Examples: Install an add-on: ha_manage_addon(slug="...", action="install") Start an add-on: ha_manage_addon(slug="...", action="start") Add a store repository: ha_manage_addon(action="add_repository", repository="https://github.com/balloob/home-assistant-addons") Remove a store repository: ha_manage_addon(action="remove_repository", repository="0f1cc410") Set add-on option: ha_manage_addon(slug="...", options={"log_level": "debug"})
Note: only the fields you provide are updated — current values are fetched first
and merged automatically. Fields not in the add-on's schema are ignored with a warning. Disable auto-update: ha_manage_addon(slug="...", auto_update=False) Change host port: ha_manage_addon(slug="...", network={"5800/tcp": 8082}) Set boot mode: ha_manage_addon(slug="...", boot="manual") Call HTTP API: ha_manage_addon(slug="...", path="/api/events") Direct port: ha_manage_addon(slug="...", path="/flows", port=1880) ESPHome list devices (HTTP): ha_manage_addon(slug="_esphome", path="/devices") ESPHome read a device's YAML (WS one-shot): ha_manage_addon(slug="_esphome", path="/ws", websocket=True, wait_for_close=False, message_limit=2, body={"command": "devices/get_config", "message_id": "1", "args": {"configuration": "device.yaml"}}) ESPHome live logs (WS, bounded): ha_manage_addon(slug="_esphome", path="/ws", websocket=True, wait_for_close=False, message_limit=60, body={"command": "devices/logs", "message_id": "1", "args": {"configuration": "device.yaml", "port": "OTA"}}) Filter WS errors only: ha_manage_addon(slug="...", path="/ws", websocket=True, python_transform="response = [m for m in response if 'ERROR' in str(m) or 'WARN' in str(m)]") HTTP subset: ha_manage_addon(slug="...", path="/flows", python_transform="response = [f['id'] for f in response]") Array-patch (Node-RED, rename a node):
ha_manage_addon(
slug="a0d7b954_nodered", path="/flows",
array_patch={"operations": [
{"op": "patch", "id": "abc123", "patches": {"name": "New Name"}},
]},
) Array-patch (Node-RED, replace one tab's nodes atomically):
ha_manage_addon(
slug="a0d7b954_nodered", path="/flows",
array_patch={"operations": [
{"op": "delete_where", "field": "z", "value": "tab-id"},
{"op": "add", "item": {"id": "n1", "type": "inject", "z": "tab-id", ...}},
{"op": "add", "item": {"id": "n2", "type": "function", "z": "tab-id", ...}},
]},
request_headers={"Node-RED-Deployment-Type": "full"},
) Custom request headers (proxy mode):
ha_manage_addon(slug="...", path="/api/state",
request_headers={"Accept": "text/plain"})
manage 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 |
| ha_list_floors_areasA | 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. Optionally project the response with fields= (top-level keys) or area_fields= (per-area-record keys, applied uniformly across nested, unassigned, and orphaned buckets). 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. |
| ha_remove_area_or_floorA | 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. |
| ha_set_area_or_floorA | Create or update a Home Assistant area or floor. Pass kind='area' (with optional floor_id, picture) 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 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="floor", name="Basement", level=-1)
ha_set_area_or_floor(kind="floor", id="ground_floor", level=0) |
| ha_get_blueprintA | Get blueprint information - list all blueprints or get details for a specific one. Without a path: Lists all installed blueprints for the specified domain.
With a path: Returns the blueprint's metadata and input definitions. The
full body (triggers/conditions/actions for automations, sequence for
scripts) is included under config ONLY when the ha_mcp_tools custom
component is installed — core's blueprint API exposes metadata alone, so
without the component the body cannot be read. EXAMPLES: List all automation blueprints: ha_get_blueprint(domain="automation") List script blueprints: ha_get_blueprint(domain="script") Get specific blueprint: ha_get_blueprint(path="homeassistant/motion_light.yaml", domain="automation")
RETURNS (when listing): List of blueprints with path, name, and domain information Count of blueprints found
RETURNS (when getting specific blueprint): Blueprint metadata (name, description, author, source_url) Input definitions with selectors and defaults config: the full parsed blueprint body (only with the ha_mcp_tools
component; !input substitution points appear as {"__input__": name})
|
| ha_import_blueprintA | Import a blueprint from a URL. Imports a blueprint from GitHub, Home Assistant Community forums,
or any direct URL to a blueprint YAML file. Set overwrite=true to
re-import a blueprint that is already installed (equivalent to the
UI's "Re-import blueprint" action) - Home Assistant then reloads all
automations/scripts that use it. EXAMPLES: SUPPORTED SOURCES: GitHub repository URLs (will be converted to raw URLs) Home Assistant Community forum posts with blueprint code Direct URLs to YAML blueprint files
RETURNS: Import result with the blueprint path where it was saved Blueprint metadata (name, domain, description) overrides_existing: true when an installed blueprint was overwritten Error details if import fails
|
| ha_report_issueA | Get diagnostic information and templates for filing issue reports or feedback. This tool generates templates for TWO types of reports: Runtime Bug Report - For ha-mcp errors, failures, unexpected behavior Agent Behavior Feedback - For AI agent inefficiency, wrong tool usage
IMPORTANT FOR AI AGENTS:
You MUST analyze the conversation context to determine which template to present: 🐛 Present RUNTIME BUG template if: User reports an error, failure, or unexpected behavior A tool returned an error or incorrect result Something is broken or not working in ha-mcp
🤖 Present AGENT BEHAVIOR template if: User mentions YOU (the agent) used the wrong tool User suggests a more efficient workflow User reports YOUR inefficiency or mistakes User says you should have done something differently
If unclear which type, ASK the user:
"Are you reporting a bug in ha-mcp, or providing feedback on how I used the tools?" WHEN TO USE THIS TOOL: OUTPUT:
Returns both templates plus diagnostic data. The full response is
LARGE (the captured logs appear in the raw log keys AND inside each
template) — pass fields=... to fetch only the keys you need once you
know which template applies. Key fields: runtime_bug_template, agent_behavior_template — pick based on context
recent_logs, startup_logs — captured ha-mcp tool/server log entries
addon_logs — addon container stdout/stderr (HA add-on installs only;
empty string otherwise)
core_error_log — Home Assistant error log (home-assistant.log) over
REST; carries auth / integration errors that don't show in addon_logs
missing_tool_hint — check this FIRST when the report is about a
missing/unavailable tool; a stale client tool list (not a bug) is the
usual cause, and refreshing the MCP connection is the fix
suggested_title, duplicate_check_urls, anonymization_guide
|
| ha_config_get_calendar_eventsA | Retrieve calendar events from a calendar entity. Retrieves calendar events within a specified time range. Parameters: entity_id: Calendar entity ID (e.g., 'calendar.family') start: Start datetime in ISO format (default: now) end: End datetime in ISO format (default: 7 days from start) max_results: Maximum number of events to return (default: 20)
Example Usage: # Get events for the next week
events = ha_config_get_calendar_events("calendar.family")
# Get events for a specific date range
events = ha_config_get_calendar_events(
"calendar.work",
start="2024-01-01T00:00:00",
end="2024-01-31T23:59:59"
)
Note: To find calendar entities, use ha_search(query='calendar', domain_filter='calendar') Returns: List of calendar events with summary, start, end, description, location
|
| ha_config_remove_calendar_eventA | 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. Parameters: entity_id: Calendar entity ID (e.g., 'calendar.family') uid: Unique identifier of the event to delete recurrence_id: Optional recurrence ID for recurring events recurrence_range: Optional recurrence range ('THIS_AND_FUTURE' to delete this and future occurrences)
Example Usage: # Delete a single event
result = ha_config_remove_calendar_event(
"calendar.family",
uid="event-12345"
)
# Delete a recurring event instance and future occurrences
result = ha_config_remove_calendar_event(
"calendar.work",
uid="recurring-event-67890",
recurrence_id="20240115T100000",
recurrence_range="THIS_AND_FUTURE"
)
Note:
To get the event UID, first use ha_config_get_calendar_events() to list events.
The UID is returned in each event's data. Returns: |
| ha_config_set_calendar_eventA | Create a new event in a calendar. 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). When NOT to use: To retrieve calendar events, use ha_config_get_calendar_events. To delete an event, use ha_config_remove_calendar_event.
Example Usage: # Create a simple event
result = ha_config_set_calendar_event(
"calendar.family",
summary="Doctor appointment",
start="2024-01-15T14:00:00",
end="2024-01-15T15:00:00"
)
# Create a recurring event (every Monday, 10 occurrences)
result = 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"
)
# Create an all-day event (date-only, no time component). The end
# date is EXCLUSIVE, so this spans 2026-07-04 through 2026-07-10.
result = ha_config_set_calendar_event(
"calendar.family",
summary="Vacation",
start="2026-07-04",
end="2026-07-11"
)
Note:
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. Because the
all-day end date is exclusive, a single-day all-day event must
set end to start + 1 day. Not every calendar integration supports event creation; recurring
events additionally require the integration to support recurrence
(the built-in Local Calendar does). Returns: |
| ha_get_camera_imageA | Retrieve a snapshot image from a Home Assistant camera entity. This tool fetches the current camera image and returns it directly for visual
analysis. Use this when you need to see what a camera is currently viewing. Parameters: entity_id: Camera entity ID (e.g., 'camera.front_door', 'camera.living_room') width: Optional width to resize the image (reduces token usage for large images) height: Optional height to resize the image
Use Cases: Security checks: "Is someone at the front door?" Pet monitoring: "Is my dog still on the couch?" Delivery verification: "Did my package get delivered?" Visual confirmation: "Did the garage door actually close?" Incident investigation: "What triggered the motion sensor?"
Example Usage: # Get current snapshot from front door camera
ha_get_camera_image(entity_id="camera.front_door")
# Get resized image to reduce token usage
ha_get_camera_image(entity_id="camera.backyard", width=640, height=480)
Notes: Only cameras exposed to Home Assistant are accessible The existing HA authentication/authorization applies Images are returned in their native format (JPEG, PNG, or GIF) Use width/height parameters for large high-resolution cameras to reduce
token usage when full resolution is not needed
Related Services: camera.snapshot: Save snapshot to file on HA server camera.turn_on/turn_off: Control camera power camera.enable_motion_detection: Enable motion detection
|
| ha_config_get_categoryA | 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. CATEGORY PROPERTIES: ID (category_id), Name Icon (optional)
EXAMPLES: List automation categories: ha_config_get_category("automation") List script categories: ha_config_get_category("script") 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. |
| ha_config_remove_categoryA | Delete a Home Assistant category. Removes the category from the category registry for the given scope
(e.g., 'automation', 'script', 'scene', 'helpers').
This will also remove the category assignment from all entities in that scope. EXAMPLES: 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. |
| ha_config_set_categoryA | 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. |
| ha_config_get_automationA | Retrieve Home Assistant automation configuration. Returns the complete configuration including triggers, conditions, actions, and mode settings. The returned config_hash is stable across consecutive reads of an unchanged config — compute_config_hash documents the underlying contract. 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: For comprehensive automation documentation, use ha_get_skill_guide. read inspect fetch view existing automation config triggers conditions actions get show detail |
| ha_config_remove_automationA | Delete a Home Assistant automation. 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: WARNING: Deleting an automation removes it permanently from your Home Assistant configuration. |
| ha_config_set_automationA | Create or update a Home Assistant automation. MUST call ha_get_skill_guide OR refer to your locally installed skills first. PREFER NATIVE SOLUTIONS OVER TEMPLATES (read this before writing any {{ ... }}):
Native triggers/conditions/actions are validated at config load, fail loudly, and
do not bypass HA's schema. Templates fail silently at runtime and obscure intent. condition: numeric_state instead of {{ states('x') | float > N }}
condition: state (with state: list) instead of {{ is_state(...) }} /
{{ states(x) in [...] }}
condition: time instead of {{ now().hour ... }} or {{ now().weekday() ... }}
condition: sun instead of {{ is_state('sun.sun', ...) }}
Native for: field on state/numeric_state triggers and state
conditions over {{ now() - X.last_changed > timedelta(...) }} duration math. wait_for_trigger instead of wait_template
choose action instead of template-based service names
For one-shot date firing, use a time trigger plus automation.turn_off on a
hardcoded entity_id — not {{ now().date() ... }}. Hardcode target.entity_id literals — never {{ this.entity_id }}.
Templates are appropriate ONLY in data.* fields, notification message/title,
event_data, and variables. The reactive best-practice checker on this tool
will surface anything in a logic position that should be native; consult the
best_practice_warnings field on the response and fix before re-submitting.
The relevant skill section is auto-embedded under skill_content on warnings,
and the full automation-patterns.md + template-guidelines.md references
ship under skill_content proactively by default. For comprehensive
guidance beyond that, call ha_get_skill_guide.
The returned automation_id is the resolved entity_id (canonical
form, e.g. automation.morning_routine) when entity registration
succeeds, falling back to the input identifier (update path) or
the generated unique_id from the upsert response (fresh create
when no identifier was passed). Before reaching for ha_config_set_automation, consider whether a
dedicated tool fits the use case better: State snapshot of one or more entities (capture-then-replay,
no trigger needed) -> ha_config_set_scene State-derived value that recomputes when its inputs change
(template sensor / binary sensor / number / select)
-> ha_config_set_helper(helper_type='template') Stateful counter / timer / schedule / boolean / etc.
-> ha_config_set_helper(helper_type='counter' | 'timer' | ...)
Supports two modes: full config replacement OR Python transformation. WHEN TO USE WHICH MODE: IMPORTANT: python_transform requires 'identifier' and 'config_hash' from ha_config_get_automation(). PYTHON TRANSFORM EXAMPLES (operate on the fetched config, which uses HA's
canonical plural root keys 'triggers'/'actions'/'conditions'): Update action: python_transform="config['actions'][0]['data']['brightness'] = 255" Add trigger: python_transform="config['triggers'].append({'trigger': 'state', 'entity_id': 'binary_sensor.motion', 'to': 'on'})" Remove last action: python_transform="config['actions'].pop()"
Creates a new automation (if identifier omitted) or updates existing automation with provided configuration. AUTOMATION TYPES: Regular Automations - Define triggers and actions directly Blueprint Automations - Use pre-built templates with customizable inputs
REQUIRED FIELDS (Regular Automations): alias: Human-readable automation name triggers: List of triggers (time, state, event, etc.) actions: List of actions to execute
REQUIRED FIELDS (Blueprint Automations): OPTIONAL CONFIG FIELDS (Regular Automations): description: Detailed description of the user's intent (RECOMMENDED: helps safely modify implementation later) category: Category ID for organization (use ha_config_get_category to list, ha_config_set_category to create) conditions: Additional conditions that must be met mode: 'single' (default), 'restart', 'queued', 'parallel' max: Maximum concurrent executions (for queued/parallel modes) initial_state: Whether automation starts enabled (true/false) variables: Variables for use in automation
BASIC EXAMPLES: Simple time-based automation:
ha_config_set_automation(config={
"alias": "Morning Lights",
"description": "Turn on bedroom lights at 7 AM to help wake up",
"triggers": [{"trigger": "time", "at": "07:00:00"}],
"actions": [{"action": "light.turn_on", "target": {"area_id": "bedroom"}}]
}) Motion-activated lighting — for: on the off-transition replaces action-delay:
ha_config_set_automation(config={
"alias": "Motion Light",
"triggers": [
{"trigger": "state", "entity_id": "binary_sensor.motion", "to": "on", "id": "motion_on"},
{"trigger": "state", "entity_id": "binary_sensor.motion", "to": "off",
"for": {"minutes": 5}, "id": "motion_off"}
],
"actions": [
{"choose": [
{"conditions": [
{"condition": "trigger", "id": "motion_on"},
{"condition": "sun", "after": "sunset"}
],
"sequence": [{"action": "light.turn_on", "target": {"entity_id": "light.hallway"}}]},
{"conditions": [{"condition": "trigger", "id": "motion_off"}],
"sequence": [{"action": "light.turn_off", "target": {"entity_id": "light.hallway"}}]}
]}
]
}) Update existing automation:
ha_config_set_automation(
identifier="automation.morning_routine",
config={
"alias": "Updated Morning Routine",
"triggers": [{"trigger": "time", "at": "06:30:00"}],
"actions": [
{"action": "light.turn_on", "target": {"area_id": "bedroom"}},
{"action": "climate.set_temperature", "target": {"entity_id": "climate.bedroom"}, "data": {"temperature": 22}}
]
}
) BLUEPRINT AUTOMATION EXAMPLES: Create automation from 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"},
"no_motion_wait": 120
}
}
}) Update blueprint automation inputs:
ha_config_set_automation(
identifier="automation.motion_light_kitchen",
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"},
"no_motion_wait": 300
}
}
}
) TRIGGER TYPES: time, time_pattern, sun, state, numeric_state, event, device, zone, template, and more
CONDITION TYPES: state, numeric_state, time, sun, template, device, zone, and more
ACTION TYPES: action calls, delays, wait_for_trigger, wait_template, if/then/else, choose, repeat, parallel For comprehensive automation documentation with all trigger/condition/action types and advanced examples: TROUBLESHOOTING: Use ha_get_state() to verify entity_ids exist Use ha_search() to find correct entity_ids IF you must use Jinja2 and have no native alternative, test it first with
ha_eval_template() before embedding it in the automation config — catches
syntax errors and unresolved entity_ids before they fail silently at runtime Use ha_search(domain_filter='automation') to find existing automations
create update modify edit automation triggers conditions actions new automation write save |
| ha_config_delete_dashboardA | 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. Accepts either the URL path or the internal dashboard ID. 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: Note: The default dashboard cannot be deleted via this method. |
| ha_config_get_dashboardA | Get dashboard info - list all dashboards, get config, or search for cards. MODE 1 — List: list_only=True
Lists every dashboard's metadata (url_path, title, icon), storage and
YAML alike (metadata only — bodies are never included here). MODE 2 — Search: any of entity_id / card_type / heading provided
Finds cards, badges, and header cards matching the criteria, including
cards nested inside stacks, grids, conditional cards, button-card
custom_fields, and state-switch states. Each match carries a
python_path and a jq_path that locate the card for nested as well as
top-level cards. The python_path is a Python subscript chain to be
appended after config — e.g.
python_transform=f'config{m["python_path"]}["icon"] = "mdi:x"' (it is
NOT valid on its own without the config prefix). jq_path is the same
location in jq dot-notation.
Multiple criteria are AND-ed. Always fetches fresh config (force=True).
Search covers cards/card/custom_fields/states containers up to a depth
bound; if the dashboard carries a non-traversed child-bearing shape
(e.g. picture-elements elements), the result carries a warnings
entry naming where, so its hidden content is not mistaken for absent.
Strategy dashboards are not searchable (no explicit cards). MODE 3 — Get: Active when list_only=False and no search parameters are provided.
Returns the full Lovelace dashboard config, defaulting to the
main dashboard if url_path is omitted.
Pass view_path=<views[].path> to return ONLY that view: the response
then carries view + view_index instead of config, keeping the
payload small on multi-view dashboards. config_hash still covers
the FULL config, so a follow-up
ha_config_set_dashboard(python_transform=...) addressing
config['views'][view_index] validates unchanged. An unknown
view_path errors and lists the available view paths. MODE 4 — Search all: mode="search" with query=
Answers "which dashboards contain this entity/card" by walking every
storage-mode dashboard's views/cards/sections for the query substring.
Each match names the url_path, view, card_path, card_type, and the
matched field/value. Takes precedence over the other modes (list_only /
entity_id / card_type / heading are ignored when mode="search").
YAML-mode dashboards are never searched on either path — the component
walk skips them in-process and the component-less legacy walk skips any
row tagged mode="yaml" — because HA resolves !secret when loading a
YAML Lovelace config, so searching one could surface resolved secrets.
On installs without the ha_mcp_tools component, the default (unnamed)
dashboard is also not searched — only dashboards with a url_path are. Return a stable config_hash (Get and Search modes only; not present in list_only mode) across consecutive reads of an unchanged config — compute_config_hash documents the underlying contract. EXAMPLES: List all dashboards: ha_config_get_dashboard(list_only=True) Get default dashboard: ha_config_get_dashboard(url_path="default") Get custom dashboard: ha_config_get_dashboard(url_path="lovelace-mobile") Get one view only: ha_config_get_dashboard(url_path="lovelace-mobile", view_path="office") Force reload: ha_config_get_dashboard(url_path="lovelace-home", force_reload=True) Find cards by entity: ha_config_get_dashboard(url_path="my-dash", entity_id="light.living_room") Find by wildcard: ha_config_get_dashboard(url_path="my-dash", entity_id="sensor.temperature_*") Find by type: ha_config_get_dashboard(url_path="my-dash", card_type="tile") Find heading: ha_config_get_dashboard(url_path="my-dash", heading="Climate", card_type="heading")
SEARCH WORKFLOW EXAMPLE: find = ha_config_get_dashboard(url_path="my-dash", entity_id="light.bedroom") ha_config_set_dashboard(
url_path="my-dash",
config_hash=find["config_hash"],
python_transform=f'config{find["matches"][0]["python_path"]}["icon"] = "mdi:lamp"'
)
Note: YAML-mode dashboards (defined in configuration.yaml) are not included in list. |
| ha_config_set_dashboardA | Create or update a Home Assistant dashboard. MUST call ha_get_skill_guide OR refer to your locally installed skills first. Creates a new dashboard or updates an existing one with the provided configuration.
Supports two modes: full config replacement OR Python transformation. Use 'default' or 'lovelace' to target the built-in default dashboard.
New dashboards require a hyphenated url_path (e.g., 'my-dashboard'). WHEN TO USE WHICH MODE: python_transform: RECOMMENDED for edits. Surgical/pattern-based updates, works on all platforms. config: New dashboards only, or full restructure. Replaces everything.
IMPORTANT: After delete/add operations, indices shift! Subsequent python_transform calls
must use fresh config_hash from ha_config_get_dashboard()
to get updated structure. Chain multiple ops in ONE expression when possible. TIP: Use ha_config_get_dashboard(entity_id=...) to get the path for any card. PYTHON TRANSFORM EXAMPLES (RECOMMENDED): Update card icon: 'config["views"][0]["cards"][0]["icon"] = "mdi:thermometer"' Add card: 'config["views"][0]["cards"].append({"type": "button", "entity": "light.bedroom"})' Delete card: 'del config["views"][0]["cards"][2]' Pattern-based update: 'for card in config["views"][0]["cards"]: if "light" in card.get("entity", ""): card["icon"] = "mdi:lightbulb"' Multi-operation: 'config["views"][0]["cards"][0]["icon"] = "mdi:a"; config["views"][0]["cards"][1]["icon"] = "mdi:b"'
MODERN DASHBOARD BEST PRACTICES: Use "sections" view type (default) with grid-based layouts Use "tile" cards as primary card type (replaces legacy entity/light/climate cards) Use "grid" cards for multi-column layouts within sections Create multiple views with navigation paths (avoid single-view endless scrolling) Use "area" cards with navigation for hierarchical organization
DISCOVERING ENTITY IDs FOR DASHBOARDS:
Do NOT guess entity IDs - use these tools to find exact entity IDs: ha_get_overview(include_entity_id=True) - Get all entities organized by domain/area ha_search(query, domain_filter, area_filter, search_types) - Find entities and config-body references in one call
If unsure about entity IDs, ALWAYS use one of these tools first. DASHBOARD DOCUMENTATION: dashboard-guide.md and dashboard-cards.md ship in this response
under skill_content by default — layout patterns,
card-type taxonomy, and worked examples. ha_get_skill_guide — deeper card-type and configuration guidance.
EXAMPLES: Create empty dashboard:
ha_config_set_dashboard(
url_path="mobile-dashboard",
title="Mobile View",
icon="mdi:cellphone"
) Create dashboard with modern sections view:
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",
"features": [{"type": "target-temperature"}]
}]
}]
}]
}
) Create strategy-based dashboard (auto-generated):
ha_config_set_dashboard(
url_path="my-home",
title="My Home",
config={
"strategy": {
"type": "home",
"favorite_entities": ["light.bedroom"]
}
}
) Note: Strategy dashboards cannot be converted to custom dashboards via this tool.
Use the "Take Control" feature in the Home Assistant interface to convert them. Update existing dashboard config:
ha_config_set_dashboard(
url_path="existing-dashboard",
config={
"views": [{
"title": "Updated View",
"type": "sections",
"sections": [{
"cards": [{"type": "markdown", "content": "Updated!"}]
}]
}]
}
) Note: When updating an existing dashboard, title/icon/require_admin/show_in_sidebar
are also updated if explicitly provided alongside (or instead of) a config change. STORAGE-MODE vs YAML-MODE DASHBOARDS:
This tool only manages storage-mode dashboards (created via UI/API and stored in
Home Assistant's storage backend). It does NOT touch YAML-defined dashboards.
Two distinct YAML cases exist and this tool covers neither: "YAML-mode" dashboards: written in their own .yaml file referenced from
configuration.yaml under lovelace: dashboards:. The dashboard itself lives
in a separate YAML file but its registration is in configuration.yaml. Dashboards inlined directly in configuration.yaml under the lovelace:
key (legacy single-dashboard mode).
For either YAML case, edit the dashboard's .yaml file directly.
ha_config_set_yaml can update the lovelace: registration
entry in configuration.yaml but does NOT touch the dashboard
body in the referenced .yaml file.
|
| ha_config_list_helpersA | List Home Assistant helpers of a specific type with their configurations. Returns one page of helpers; total_count and has_more report the full
set. Each record carries the complete configuration for its helper,
including: 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 (min/max for input_number, options for input_select, etc.) Area and label assignments
For a helper renamed in the UI, id/original_name keep the storage values while
entity_id/name reflect the current entity registry (entity_id is the identifier
ha_config_set_helper resolves against, so prefer it over id for a renamed helper).
entity_id/original_name are present only for storage-collection helpers matched in
the entity registry — types with no backing entity (e.g. tag), and every record when
the registry read degrades, carry only id/name (a warning flags the degraded case). SUPPORTED HELPER TYPES: input_button: Virtual buttons for triggering automations input_boolean: Toggle switches/checkboxes input_select: Dropdown selection lists input_number: Numeric sliders/input boxes input_text: Text input fields input_datetime: Date/time pickers counter: Counters with increment/decrement/reset timer: Countdown timers with start/pause/cancel schedule: Weekly schedules with time ranges (on/off per day) zone: Geographical zones for presence detection person: Person entities linked to device trackers tag: NFC/QR tags for automation triggers
EXAMPLES: List all number helpers: ha_config_list_helpers("input_number") List all counters: ha_config_list_helpers("counter") List all zones: ha_config_list_helpers("zone") List all persons: ha_config_list_helpers("person") List all tags: ha_config_list_helpers("tag") List every helper type at once: ha_config_list_helpers("all") Next page: ha_config_list_helpers("input_boolean", offset=100)
NOTE: Storage types list what HA's {type}/list command returns:
the storage-backed helpers (created via UI/API), not the YAML-defined
ones. person is the exception — HA lists its YAML-configured persons
alongside the storage ones, so both appear here. Flow-based types (template / group / utility_meter / derivative / etc.)
require the ha_mcp_tools custom component (>= 1.1.0) and are served only
through it; storage types are listed on all installs. Requesting a flow
type without the component returns a COMPONENT_NOT_INSTALLED error. Pass helper_type="all" to enumerate every helper type in a single call.
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. For detailed helper documentation, use ha_get_skill_guide. list all helpers input_boolean input_number input_text counter timer input_datetime input_select |
| ha_config_set_helperA | Create or update Home Assistant helper entities and config subentries
(28 types, unified interface). MUST call ha_get_skill_guide OR refer to your locally installed skills first. SIMPLE/FLOW helper create requires name; SIMPLE/FLOW helper update
requires helper_id. Config subentry create requires entry_id and
subentry_type; config subentry update also requires subentry_id. SIMPLE types (structured params, WebSocket API): input_boolean, input_button,
input_select, input_number, input_text, input_datetime, counter, timer, schedule,
zone, person, tag. FLOW types (pass config dict, Config Entry Flow API): template, group,
utility_meter, derivative, min_max, threshold, integration, statistics, trend,
random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat.
Note: tod is the purpose-built "is-current-time-in-range" indicator
(supports cross-midnight ranges, unlike schedule). CONFIG_SUBENTRY type (Config Subentry Flow API): config_subentry.
Pass entry_id, subentry_type, and config. Pass subentry_id to
reconfigure an existing subentry; omit it to create a new subentry. For flow-type updates, pass the existing entry_id as helper_id. Options flows
reject the name key on update — to rename a flow helper, delete and recreate. Behavior notes: UPDATE preserves type-specific fields not re-passed (rename never wipes
initial/icon/etc. for any simple helper). Pass action="create" or action="update" to disambiguate intent.
For SIMPLE/FLOW helpers, omitted action falls back to the implicit
helper_id-presence discriminator. For config subentries, omitted
action falls back to the subentry_id-presence discriminator. For flow-based helpers, config keys not declared by any step's
data_schema are silently ignored by HA; submit once and the
validation error returns the data_schema for that helper so
subsequent calls use the correct field names. Validation errors raised by this tool carry the helper's
data_schema in the response context (and menu_options for
menu-rooted helpers like template/group when no sub-type is
chosen yet) so a follow-up call can self-correct without a
separate schema-discovery round-trip. Flows that present more than one menu (e.g. an MQTT device
subentry reconfigure looping through its summary menu) take
next_step_id as a LIST of successive selections, consumed one
per menu encounter.
EXAMPLES (menu-based types + tod, where first-call payload is non-obvious): template sensor:
ha_config_set_helper(helper_type="template", name="Room Temp",
config={"next_step_id": "sensor",
"state": "{{ states('sensor.x')|float }}",
"unit_of_measurement": "°C"}) group (light):
ha_config_set_helper(helper_type="group", name="Kitchen Lights",
config={"group_type": "light",
"entities": ["light.a", "light.b"]}) tod (time-of-day indicator, cross-midnight OK):
ha_config_set_helper(helper_type="tod", name="Quiet Hours",
config={"after_time": "22:00:00", "before_time": "07:00:00"}) config subentry (create under an existing integration):
ha_config_set_helper(helper_type="config_subentry",
entry_id="01HXYZ...", subentry_type="conversation",
config={"name": "Local agent", "model": "gemma3:27b"})
helper-selection.md ships in this response under
skill_content by default — decision
matrix for picking the right helper type plus worked examples
and per-type field tables. For deeper helper-design guidance
beyond what ships here, call ha_get_skill_guide.
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 |
| ha_config_get_sceneA | Retrieve Home Assistant scene configuration. Returns the complete configuration for a scene, including the entities
dict and other settings (name, icon, id). EXAMPLES: RELATED TOOLS: For detailed scene configuration help, use ha_get_skill_guide. |
| ha_config_remove_sceneA | Delete a Home Assistant scene. EXAMPLES: IMPORTANT LIMITATION:
This tool can only delete scenes created via the Home Assistant UI.
Scenes defined in YAML configuration files (scenes.yaml or configuration.yaml)
cannot be deleted through the API and will return a 405 Method Not Allowed error. To remove YAML-defined scenes, you must edit the configuration file directly. WARNING: Deleting a scene that is referenced by automations or scripts
(via scene.turn_on) may cause those to fail. |
| ha_config_set_sceneA | 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. WHEN TO USE: python_transform: surgical edits to an existing scene
(add/remove/update a single entity entry). Requires config_hash
from ha_config_get_scene() for optimistic locking.
config: creating a new scene, or wholesale replacement.
WHEN NOT TO USE: To activate a scene at runtime, use ha_call_service(domain="scene",
service="turn_on", target=...) — this tool only manages scene
configuration, not the runtime turn-on/off side. To list or look up existing scenes, use
ha_search(domain_filter="scene").
SCENE SHAPE: entities is a dict keyed by entity_id (e.g.,
{'light.kitchen': {'state': 'on', 'brightness': 200}}), NOT a
list. 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. |
| ha_config_get_scriptA | Retrieve Home Assistant script configuration. Returns the complete configuration for a script, including sequence, mode, fields, and other settings. The returned config_hash is stable across consecutive reads of an unchanged config — compute_config_hash documents the underlying contract. 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. A leading script. prefix on the input is stripped before lookup — behavioral parity with ha_config_get_automation (mechanism differs: automations resolve via state lookup; scripts strip the prefix). EXAMPLES: For detailed script configuration help, use ha_get_skill_guide. read inspect fetch view existing script config sequence actions get show detail |
| ha_config_remove_scriptA | Delete a Home Assistant script. EXAMPLES: IMPORTANT LIMITATION:
This tool can only delete scripts created via the Home Assistant UI.
Scripts defined in YAML configuration files (scripts.yaml or configuration.yaml)
cannot be deleted through the API and will return a 405 Method Not Allowed error. To remove YAML-defined scripts, you must edit the configuration file directly. WARNING: Deleting a script that is used by automations may cause those automations to fail. |
| ha_config_set_scriptA | Create or update a Home Assistant script. MUST call ha_get_skill_guide OR refer to your locally installed skills first. PREFER NATIVE ACTIONS OVER TEMPLATES (read this before writing any {{ ... }}):
Native actions are validated at config load, fail loudly, and do not bypass HA's
schema. Templates in logic positions fail silently and obscure intent. choose / if/then/else instead of template-based service names
wait_for_trigger instead of wait_template
Native for: field on state conditions inside choose/if, and on
state/numeric_state triggers in wait_for_trigger, instead of
{{ now() - X.last_changed > timedelta(...) }} duration math. repeat with for_each instead of template loops
Hardcode target.entity_id literals — never {{ this.entity_id }}.
Templates are appropriate ONLY in data.* fields, notification message/title,
event_data, and variables. The reactive best-practice checker on this tool
will surface anything in a logic position that should be native; consult the
best_practice_warnings field on the response and fix before re-submitting.
The relevant skill section is auto-embedded under skill_content on warnings,
and the full automation-patterns.md + template-guidelines.md references
ship under skill_content proactively by default. For comprehensive
guidance beyond that, call ha_get_skill_guide.
Supports two modes: full config replacement OR Python transformation. WHEN TO USE WHICH MODE: IMPORTANT: python_transform requires 'config_hash' from ha_config_get_script(). PYTHON TRANSFORM EXAMPLES: Update step: python_transform="config['sequence'][0]['data']['message'] = 'Hello'" Add step: python_transform="config['sequence'].append({'delay': {'seconds': 5}})" Remove last step: python_transform="config['sequence'].pop()"
Creates a new script or updates an existing one with the provided configuration.
Supports both regular scripts (with sequence) and blueprint-based scripts. Required config fields (choose one):
- sequence: List of actions to execute (for regular scripts)
- use_blueprint: Blueprint configuration (for blueprint-based scripts) Optional config fields:
- alias: Display name (defaults to script_id)
- description: Script description
- icon: Icon to display
- mode: Execution mode ('single', 'restart', 'queued', 'parallel')
- max: Maximum concurrent executions (for queued/parallel modes)
- fields: Input parameters for the script SCRIPTS vs AUTOMATIONS: Scripts use 'sequence', NOT 'trigger' or 'action'.
If you need trigger-based execution, use ha_config_set_automation instead. EXAMPLES: Create basic delay script:
ha_config_set_script(script_id="wait_script", config={
"sequence": [{"delay": {"seconds": 5}}],
"alias": "Wait 5 Seconds",
"description": "Simple delay script"
}) Create service call script:
ha_config_set_script(script_id="blink_light", config={
"sequence": [
{"action": "light.turn_on", "target": {"entity_id": "light.living_room"}},
{"delay": {"seconds": 2}},
{"action": "light.turn_off", "target": {"entity_id": "light.living_room"}}
],
"alias": "Light Blink",
"mode": "single"
}) Create script with parameters:
ha_config_set_script(script_id="backup_script", config={
"alias": "Backup with Reference",
"description": "Create backup with optional reference parameter",
"fields": {
"reference": {
"name": "Reference",
"description": "Optional reference for backup identification",
"selector": {"text": None}
}
},
"sequence": [
{
"action": "hassio.backup_partial",
"data": {
"compressed": False,
"homeassistant": True,
"homeassistant_exclude_database": True,
"name": "Backup_{{ reference | default('auto') }}{{ now().strftime('%Y%m%d%H%M%S') }}"
}
}
]
}) Update script:
ha_config_set_script(script_id="morning_routine", config={
"sequence": [
{"action": "light.turn_on", "target": {"area_id": "bedroom"}},
{"action": "climate.set_temperature", "target": {"entity_id": "climate.bedroom"}, "data": {"temperature": 22}}
],
"alias": "Updated Morning Routine"
}) Create blueprint-based script:
ha_config_set_script(script_id="notification_script", config={
"alias": "My Notification Script",
"use_blueprint": {
"path": "notification_script.yaml",
"input": {
"message": "Hello World",
"title": "Test Notification"
}
}
}) Update blueprint script inputs:
ha_config_set_script(script_id="notification_script", config={
"alias": "My Notification Script",
"use_blueprint": {
"path": "notification_script.yaml",
"input": {
"message": "Updated message",
"title": "Updated Title"
}
}
}) Note: Scripts use Home Assistant's action syntax. Check the documentation for advanced
features like conditions, variables, parallel execution, and service call options. create update modify edit script sequence actions new script write save |
| ha_manage_energy_prefsA | Manage the Home Assistant Energy Dashboard preferences. The Energy Dashboard configuration (grid/solar/battery/gas/water energy
sources, individual device consumption sensors for electricity and
water, cost tariffs) is stored in .storage/energy and not otherwise
reachable via REST, services, or helper flows — this tool is the only
way for agents to inspect or modify it. WHEN TO USE: mode='get' / 'set': inspect or replace the full Energy Dashboard
config. Use 'set' for bulk edits or anything touching multiple
top-level keys at once. mode='add_device' / 'remove_device': add or remove a single
device-consumption entry. The tool performs a fresh read-modify-write
internally; the caller does NOT manage config_hash. Use water=True
to target the water meter list instead of electricity. mode='add_source': append a single entry to energy_sources (grid,
solar, battery, gas, or water). Same atomic read-modify-write
semantics.
WHEN NOT TO USE: CAVEATS: energy/save_prefs has per-key FULL-REPLACE semantics. Passing
{"device_consumption": [<one entry>]} deletes every other device
the user had configured — silently, with no error. mode='set'
requires a fresh config_hash for optimistic locking; convenience
modes hide this entirely.
config_hash accepts both a single str (full-blob lock) and
a dict[_PrefsKey, str] keyed by top-level keys (per-key lock,
taken from the config_hash_per_key field of the mode='get'
response). The per-key form lets an agent submit only the top-
level key it wants to change — set-equality between config
keys and dict keys is enforced, and any key outside the canonical
set (typo, etc.) on either side is rejected with
VALIDATION_FAILED rather than silently dropped (so an empty
submission cannot succeed as a no-op). A per-key submission
still fully replaces that key's value as the save endpoint
requires. Mismatch on any locked key returns RESOURCE_LOCKED
with the offending keys in the response's top-level
mismatched_keys (create_error_response flattens the
context dict onto the response root).
dry_run=True skips the hash check entirely for both forms;
the per-key form is therefore silently accepted on dry runs even
if its keys would mismatch the current state.
A local shape check runs before every write; malformed payloads
are rejected with a shape_errors list. After a successful write, the tool calls energy/validate and
returns any residual issues as post_save_validation_errors in
the response. These reflect semantic problems (missing stats, unit
mismatches) that shape checks can't catch; the save persists
regardless — correct the config and write again if needed. The underlying save endpoint is admin-only. Non-admin tokens will
receive an authorization error from Home Assistant. Convenience modes are NOT idempotent: 'add_device' on an existing
stat_consumption returns RESOURCE_ALREADY_EXISTS; 'remove_device'
on a missing entry returns RESOURCE_NOT_FOUND. 'add_source' rejects
duplicates by (type, stat_energy_from) for solar/battery/gas/water
(RESOURCE_ALREADY_EXISTS); grid entries are appended without a
duplicate check (multiple grid variants are legitimate, and grid
has no single canonical uniqueness key) — the caller is responsible
for de-duplicating grid sources. Convenience modes do NOT bypass the local shape check on dry_run:
dry_run=True still raises RESOURCE_ALREADY_EXISTS
(duplicate add_device / add_source), RESOURCE_NOT_FOUND
(missing remove_device), or VALIDATION_FAILED (post-mutator
shape error) when the proposed mutation is not applicable. The
mutator and shape check both run before the dry-run short-circuit.
|
| ha_get_entityA | Get entity registry information for one or more entities. Returns detailed entity registry metadata including area assignment,
custom name/icon, enabled/hidden state, aliases, labels, and more. 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). RELATED TOOLS: ha_set_entity(): Modify entity properties (area, name, icon, enabled, hidden, aliases) ha_get_state(): Get current state/attributes (on/off, temperature, etc.) ha_search(): Find entities by name, domain, or area
EXAMPLES: Single entity: ha_get_entity("sensor.temperature") Multiple entities: ha_get_entity(["light.living_room", "switch.porch"])
RESPONSE FIELDS: entity_id: Full entity identifier name: Custom display name (null if using original_name) original_name: Default name from integration icon: Custom icon (null if using default) area_id: Assigned area/room ID (null if unassigned) disabled_by: Why disabled (null=enabled, "user"/"integration"/etc) hidden_by: Why hidden (null=visible, "user"/"integration"/etc) enabled: Boolean shorthand (True if disabled_by is null) hidden: Boolean shorthand (True if hidden_by is not null) aliases: Voice assistant aliases labels: Assigned label IDs categories: Category assignments (dict mapping scope to category_id) device_class: User "Show As" override (null = use original_device_class) original_device_class: Default device class from the integration options: Per-domain registry options (e.g. sensor display_precision).
Voice-assistant exposure is also stored here but should be set/cleared
via the ha_set_entity(expose_to=...) parameter, not the options dict. platform: Integration platform (e.g., "hue", "zwave_js") device_id: Associated device ID (null if standalone) config_entry_id: Parent config entry's ID (null for YAML-only
entities). When 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. unique_id: Integration's unique identifier
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;
resolves area_id above) floor: Floor NAME of the assigned area label_names: Assigned label NAMES (resolves the label ids in labels)
Resolved label names live under label_names HERE (this tool's base
labels already carries the label ids); ha_search result_fields and
ha_get_entity_exposure instead emit the resolved names under labels.
get entity state attributes details single specific entity_id |
| ha_remove_entityA | Remove one or more entities from the Home Assistant entity registry. Permanently removes the entity registration from Home Assistant.
The entity will no longer appear in the UI or be available to automations. WARNING: This permanently removes the entity registration. 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 action cannot be undone without restoring from backup
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") Remove stale helper entry: ha_remove_entity("input_boolean.deleted_helper") Bulk cleanup: ha_remove_entity(["sensor.orphan_1", "sensor.orphan_2"])
NOTE: For most use cases, consider disabling instead:
ha_set_entity(entity_id="sensor.old", enabled=False) RELATED TOOLS: |
| ha_set_entityA | Update entity properties in the entity registry. Allows modifying entity metadata such as area assignment, display name,
icon, "Show As" device class override, per-domain registry options,
enabled/disabled state, visibility, aliases, labels, voice assistant
exposure, and entity_id rename in a single call. BULK OPERATIONS:
When entity_id is a list, only labels, expose_to, and categories parameters are supported.
Other parameters (area_id, name, icon, device_class, options, enabled, hidden, aliases, new_entity_id, new_device_name) require single entity. LABEL OPERATIONS: label_operation="set" (default): Replace all labels with the provided list. Use [] to clear. label_operation="add": Add labels to existing ones without removing any. label_operation="remove": Remove specified labels from the entity.
SHOW AS / DEVICE CLASS:
device_class overrides the entity's display device class — equivalent to the
HA UI's "Show As" dropdown. Use empty string '' to clear. Applies instantly,
no reload needed. REGISTRY OPTIONS:
options carries per-domain registry options (sensor display_precision,
weather forecast_type, etc). Pass {domain: {key: value}}; multi-domain
dicts 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:
Use new_entity_id to change an entity's ID (e.g., sensor.old -> sensor.new).
Domain must match. 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:
Use new_device_name to rename the associated device. 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:
Single entity: Assign to area: ha_set_entity("sensor.temp", area_id="living_room") Rename display name: ha_set_entity("sensor.temp", name="Living Room Temperature") Set Show As: ha_set_entity("binary_sensor.zone_10", device_class="window") Clear Show As: ha_set_entity("binary_sensor.zone_10", device_class="") Set sensor precision: ha_set_entity("sensor.power", options={"sensor": {"display_precision": 2}}) Rename entity_id: ha_set_entity("light.old_name", new_entity_id="light.new_name") Rename entity and device: ha_set_entity("light.old", new_entity_id="light.new", new_device_name="New Lamp") Rename entity_id with friendly name: ha_set_entity("sensor.old", new_entity_id="sensor.new", name="New Name") Set labels: ha_set_entity("light.lamp", labels=["outdoor", "smart"]) Add labels: ha_set_entity("light.lamp", labels=["new_label"], label_operation="add") Remove labels: ha_set_entity("light.lamp", labels=["old_label"], label_operation="remove") Clear labels: ha_set_entity("light.lamp", labels=[]) Expose to Alexa: ha_set_entity("light.lamp", expose_to={"cloud.alexa": True})
Bulk operations: Set labels on multiple: ha_set_entity(["light.a", "light.b"], labels=["outdoor"]) Add labels to multiple: ha_set_entity(["light.a", "light.b"], labels=["new"], label_operation="add") Expose multiple to Alexa: ha_set_entity(["light.a", "light.b"], expose_to={"cloud.alexa": True})
ENABLED/DISABLED WARNING:
Setting enabled=False performs a registry-level disable — the entity is completely
removed from the Home Assistant state machine and hidden from the UI. It 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. Use these instead: ha_call_service("automation", "turn_off", entity_id="automation.xxx") ha_call_service("script", "turn_off", entity_id="script.xxx")
|
| ha_config_list_groupsA | List Home Assistant entity groups with their member entities. Returns one page of groups created via group.set service or YAML
configuration; total_count and has_more report the full set. Each
group includes: EXAMPLES: NOTE: This returns old-style groups (created via group.set or YAML).
Platform-specific groups (light groups, cover groups) are separate entities. |
| ha_config_remove_groupA | 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: 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.
|
| ha_config_set_groupA | 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. EXAMPLES: Create group: ha_config_set_group("bedroom_lights", entities=["light.lamp", "light.ceiling"]) Create with name: ha_config_set_group("sensors", entities=["sensor.temp"], name="All Sensors") Update name: ha_config_set_group("lights", name="Living Room Lights") Add entities: ha_config_set_group("lights", add_entities=["light.extra"]) Remove entities: ha_config_set_group("lights", remove_entities=["light.old"]) Replace all entities: ha_config_set_group("lights", entities=["light.new1", "light.new2"])
NOTE: entities, add_entities, and remove_entities are mutually exclusive. |
| ha_get_hacs_infoA | 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. repository_id accepts a numeric HACS ID or an
owner/repo path. |
| ha_manage_hacsA | Manage HACS (Home Assistant Community Store) — install/update, remove, add custom repositories, or refresh repository information. Use action="download" to install or update a repository,
action="remove" to uninstall a downloaded repository, or
action="add_repository" to register a custom GitHub repository with HACS. 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. repository_id accepts a
numeric HACS ID or an owner/repo path; 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. |
| ha_get_historyA | Retrieve historical data from Home Assistant's recorder. Sources: "history" (default): Raw state changes, ~10 day retention, full resolution "statistics": Pre-aggregated data, permanent retention, requires state_class
Shared params: entity_ids, start_time, end_time, limit, offset
History params: minimal_response, significant_changes_only
Statistics params: period, statistic_types Default time range: 24h for history, 30 days for statistics Use ha_get_history (default) when: Troubleshooting why a value changed ("Why was my bedroom cold last night?") Checking event sequences ("Did my garage door open while I was away?") Analyzing recent patterns ("What time does motion usually trigger?")
Use ha_get_history(source="statistics") when: Tracking long-term trends beyond 10 days ("Energy use this month vs last month?") Computing period averages ("Average living room temperature over 6 months?") Entities must have state_class (measurement, total, total_increasing)
WARNING: limit and offset apply per entity (not globally across all entities).
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. Example -- history (default): ha_get_history(entity_ids="sensor.bedroom_temperature", start_time="24h")
ha_get_history(entity_ids=["sensor.temperature", "sensor.humidity"], start_time="7d", limit=500)
# Default order="desc" returns newest states first.
# To paginate oldest-first, use order="asc":
ha_get_history(entity_ids="sensor.temperature", start_time="7d", limit=100, offset=100, order="asc")
Example -- statistics: ha_get_history(source="statistics", entity_ids="sensor.total_energy_kwh", start_time="30d", period="day")
ha_get_history(source="statistics", entity_ids="sensor.living_room_temperature",
start_time="6m", period="month", statistic_types=["mean", "min", "max"])
ha_get_history(source="statistics", entity_ids="sensor.energy_kwh",
start_time="30d", period="5minute", limit=100, offset=200)
|
| ha_get_integrationA | 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 all integrations: ha_get_integration() Paginate: ha_get_integration(offset=50) Search: ha_get_integration(query="zigbee") Get specific entry: ha_get_integration(entry_id="abc123") Get entry with editable fields: ha_get_integration(entry_id="abc123", include_schema=True) Get entry with diagnostics dump: ha_get_integration(entry_id="abc123", include_diagnostics=True) Get device-scoped diagnostics: ha_get_integration(entry_id="abc123", include_diagnostics=True, device_id="dev123") Get the parsed KNX ETS project (group-address table): ha_get_integration(entry_id="", include_knx_project=True) Walk a sub-tree: ha_get_integration(entry_id="abc123", include_diagnostics=True, diagnostics_data_path="") Paginate a large list: ha_get_integration(entry_id="abc123", include_diagnostics=True, diagnostics_data_path="", diagnostics_data_limit=10, diagnostics_data_offset=20) List config subentries: ha_get_integration(entry_id="abc123", include_subentries=True) Inspect subentry create schema: ha_get_integration(entry_id="abc123", include_subentry_schema=True, subentry_type="conversation") Inspect subentry reconfigure schema: ha_get_integration(entry_id="abc123", include_subentry_schema=True, subentry_type="conversation", subentry_id="sub123") List template entries: 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; a field that
was never set may be absent (rather than shown at its schema default).
Values that match a secrets.yaml entry are returned as
"**redacted**". Use include_schema=True to see every editable
field and its default/type. Nested option sections (e.g. a template
helper's advanced_options) are additively flattened one level —
each section's leaf keys are copied to the top of options (mirroring
the OptionsFlow-derived read) while the raw nested section is preserved
for fidelity, and an existing top-level key is never overwritten. Each entry carries: log_level: the canonical Python logger level name
(DEBUG/INFO/WARNING/ERROR/CRITICAL) when the
integration has a logger.set_level override, or "DEFAULT"
(uppercase sentinel) when no override is set.
log_level_raw: the original numeric level (e.g. 10 for DEBUG)
when HA returned an int, None otherwise (no override set, or HA
provided a level name as a string).
This is distinct from the add-on side, where ha_get_addon returns
Supervisor's lowercase "default" literal — do not cross-compare. |
| ha_remove_helpers_integrationsA | Remove a Home Assistant helper or integration config entry. Unifies three backend removal mechanisms — simple-helper websocket
delete, config-entry delete, and config-subentry delete — behind one
entry point with four routing paths driven by helper_type. WHEN NOT TO USE: Removing only an entity (without deleting its underlying helper or
config entry) — use ha_remove_entity instead. YAML-configured helpers — they have no storage backend. Edit the
YAML file and reload the relevant integration.
SUPPORTED HELPER TYPES: SIMPLE (12, websocket-delete): input_button, input_boolean,
input_select, input_number, input_text, input_datetime, counter,
timer, schedule, zone, person, tag. FLOW (15, config-entry-delete via entity lookup): template, group,
utility_meter, derivative, min_max, threshold, integration,
statistics, trend, random, filter, tod, generic_thermostat,
switch_as_x, generic_hygrostat.
ROUTING: SIMPLE helper_type + bare helper_id or entity_id → websocket delete. FLOW helper_type + entity_id → resolve entity_id to config_entry_id
via entity_registry, then delete the config entry. All sub-entities
(e.g. utility_meter tariffs) are removed together. helper_type=None + entry_id → direct config entry delete (any
integration). helper_type="config_subentry" + parent entry_id + subentry_id →
delete one config subentry.
MISSING-TARGET CONTRACT:
A target that is confirmed absent raises a structured error
rather than returning silent success, so a typo'd or stale
identifier surfaces immediately at the caller layer (the
success boolean is what agent wrappers branch on). The
error code per-path follows the target shape: SIMPLE (bare helper_id or entity_id): state-machine empty AND
entity registry empty → raises ENTITY_NOT_FOUND. FLOW (entity_id): not in entity registry → raises
ENTITY_NOT_FOUND. YAML-configured helpers (no config entry
backing) raise RESOURCE_NOT_FOUND. A bare helper_id (no
.) on a FLOW target raises ENTITY_NOT_FOUND — FLOW
resolution needs a full entity_id. TOCTOU 404 on the
resolved entry_id raises RESOURCE_NOT_FOUND. Direct config entry (helper_type=None): backend returns HTTP
404 → raises RESOURCE_NOT_FOUND. Config subentry: backend returns a "not_found" error → raises
RESOURCE_NOT_FOUND.
Idempotency at the contract level still holds (call N times =
same response). Transient connectivity failures (WebSocket
disconnected, network timeouts) raise their own codes
(WEBSOCKET_DISCONNECTED, CONNECTION_FAILED) so retry
logic can branch separately. EXAMPLES: Remove SIMPLE button:
ha_remove_helpers_integrations(
target="my_button", helper_type="input_button", confirm=True
) Remove FLOW utility_meter (any sub-entity works):
ha_remove_helpers_integrations(
target="sensor.energy_peak",
helper_type="utility_meter",
confirm=True,
) Remove any integration by entry_id:
ha_remove_helpers_integrations(
target="01HXYZ...", confirm=True
) Remove a config subentry:
ha_remove_helpers_integrations(
target="01HXYZ...", helper_type="config_subentry",
subentry_id="subentry-123", confirm=True
)
WARNING: Removing a helper or integration that is referenced by
automations, scripts, or other integrations may cause those to fail.
Use ha_search() / ha_get_integration() to verify before
removal. Cannot be undone. |
| ha_set_integrationA | Manage an integration (config entry): enable/disable, add, or update options. 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).
WHEN NOT TO USE: Helpers (template, group, utility_meter, ...): use
ha_config_set_helper. 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. 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 with a structured error instead of completing. EXAMPLES: Disable: ha_set_integration(entry_id="abc123", enabled=False) Add: ha_set_integration(domain="workday", config={"name": "Workday"}) Update options: ha_set_integration(entry_id="abc123", config={"scan_interval": 30})
|
| ha_config_get_labelA | 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. LABEL PROPERTIES: EXAMPLES: Use ha_config_set_label() to create or update labels.
Use ha_set_entity(labels=["label1", "label2"]) to assign labels to entities. |
| ha_config_remove_labelA | 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: 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. |
| ha_config_set_labelA | 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 flexible tagging system that can be applied to entities,
devices, and areas for organization and automation purposes. EXAMPLES: Create simple label: ha_config_set_label("Critical") Create colored label: ha_config_set_label("Outdoor", color="green") Create label with icon: ha_config_set_label("Battery Powered", icon="mdi:battery") Create full label: ha_config_set_label("Security", color="red", icon="mdi:shield", description="Security-related devices") Update label: ha_config_set_label("Updated Name", label_id="my_label_id", color="blue")
After creating a label, use ha_set_entity(labels=["label_id"]) to assign it to entities. |
| ha_manage_radioA | Manage Home Assistant radios — Z-Wave, Zigbee, Matter, and Thread. For read-only inspection prefer ha_get_device / ha_get_system_health,
which mirror the 'diagnostics' and 'network_status' actions; use this
tool for writes and the active 'ping' probe (unique to this tool). Write
actions perform inclusion/commissioning, removal, healing,
reconfiguration, firmware updates and credential provisioning. Caveats: destructive actions (e.g. remove_device, network restore,
change_channel, hard_reset, remove_fabric) require confirm=True.
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_get_deviceA | 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): 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. |
| ha_remove_deviceA | 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
This uses the config entry removal which is the safe way to remove devices.
If the device has multiple config entries, they must all be removed. EXAMPLES: NOTE: For most use cases, consider disabling the device instead:
ha_set_device(device_id="abc123", disabled_by="user") |
| ha_set_deviceA | 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
PARAMETERS: name: Sets the user-defined display name (name_by_user) area_id: Assigns device to an area/room. Use '' to remove from area. disabled_by: Set to 'user' to disable, or empty to enable labels: List of labels (replaces existing labels)
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"])
|
| ha_config_delete_dashboard_resourceA | 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. |
| ha_config_list_dashboard_resourcesA | List Lovelace dashboard resources (custom cards, themes, CSS/JS). Returns one page of registered resources; total_count and has_more
report the full set. 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. inline_count and by_type summarise every resource, not just this page.
|
| ha_config_set_dashboard_resourceA | 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) url: External resource URL (/local/, /hacsfiles/, or https://...)
INLINE MODE (content=): Custom card code written inline CSS styling for dashboards Self-contained files up to ~128KB URLs are deterministic (same content = same URL) 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.) Supports 'module' and 'css' types only (not 'js')
URL MODE (url=): Files in /config/www/ directory (/local/...) HACS-installed cards (/hacsfiles/...) External CDN resources (https://...) Supports all types: 'module', 'js', 'css'
RESOURCE TYPES: module: ES6 JavaScript modules (recommended for custom cards) js: Legacy JavaScript files (older custom cards, url mode only) css: CSS stylesheets (themes, global styles)
EXAMPLES: Inline custom card:
ha_config_set_dashboard_resource(
content="""
class MyCard extends HTMLElement {
setConfig(config) { this.config = config; }
set hass(hass) {
this.innerHTML = <ha-card>Hello ${hass.states[this.config.entity]?.state}</ha-card>;
}
}
customElements.define('my-card', MyCard);
""",
resource_type="module"
) Add custom card from www/ directory:
ha_config_set_dashboard_resource(
url="/local/my-custom-card.js",
resource_type="module"
) Add HACS card (after installing via ha_manage_hacs(action='download')):
ha_config_set_dashboard_resource(
url="/hacsfiles/lovelace-mushroom/mushroom.js",
resource_type="module"
) Update existing resource:
ha_config_set_dashboard_resource(
url="/local/my-card-v2.js",
resource_type="module",
resource_id="abc123"
) Note: After adding a resource, clear browser cache or hard refresh
(Ctrl+Shift+R) to load changes. |
| ha_get_overviewA | 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.
Standard/full modes paginate entities (default 200 per page) — use offset
to fetch more. Use 'domains' filter to narrow scope. Use fields= to project the response to only the keys you need — a
significantly smaller payload when fetching a single sub-section (e.g.
fields=["system_info"] returns just that section instead of the full overview). When (and only when) the ha-mcp settings-UI sidecar is running
(stdio mode, e.g. Claude Desktop / Claude Code), the response
includes a settings_url field — the local URL to the
tool-configuration page. Hand this URL to the user when they
ask how to enable or disable tools or change server settings.
settings_url is emitted regardless of fields=
projection (so it stays discoverable even when callers
minimize the response) but only when the sidecar URL file
actually exists. In HTTP / Docker / OAuth modes there is no sidecar URL file and the
server can't know its externally reachable host, so the response
instead carries a settings_url_hint string telling the user where
the page is mounted and to read the full URL from the startup logs.
Hand whichever of the two fields is present to the user. The response also carries an ha_mcp_update object
{current, latest, update_available} reporting whether a newer ha-mcp
release is available (PyPI for pip/Docker, the Supervisor add-on store
for the add-on) — proactively tell the user when update_available is
true. Emitted regardless of fields=; omitted only for the
unknown version and when HA_MCP_DISABLE_UPDATE_CHECK is set. |
| ha_get_stateA | Get current status, state, and attributes of one or more entities (lights, switches, sensors, climate, covers, locks, fans, etc.). SINGLE ENTITY:
Pass a string entity_id. Returns the entity's full state and attributes. MULTIPLE ENTITIES:
Pass a list of entity IDs (max 100). Efficiently retrieves states using
parallel requests. Duplicates are automatically deduplicated.
Returns success=True if at least one entity state was retrieved.
Check 'error_count' for any failed lookups in partial-success scenarios. FIELDS PROJECTION:
fields= projects the per-entity record keys (see the fields= parameter
description for the full key list), NOT the outer bulk response wrapper.
In single-entity mode it filters keys of the returned record directly. In bulk
mode it filters keys of each record inside states[entity_id]; 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. When attribute_keys= is set but has no effect (because attributes was
excluded by fields=), a warnings list is emitted outside the projected
entity record(s): in bulk mode at the response wrapper level (sibling of
success/count/states); in single-entity mode at the top-level result
(sibling of data/metadata, since the projected record IS data).
The warnings list is never a record key, so fields=["state"] returns a
record with only state regardless of whether the no-effect warning fires. EXAMPLES: Single: ha_get_state("light.kitchen") Multiple: ha_get_state(["light.kitchen", "light.living_room", "sensor.temperature"]) State only: ha_get_state("light.kitchen", fields=["state"]) 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 |
| ha_searchA | 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;
omit query to enumerate a domain, area, or state. automations / scripts / scenes / helpers / dashboards: matches
inside config definitions — triggers, actions, sequences, scene
entity-sets, helper bodies, dashboard cards. Driven by query;
narrow with search_types.
Use this whenever you need to find something in HA without deciding
entity-name vs config-body search up front. 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 add-ons: use ha_get_addon.
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. Pass
search_types=[...] to force config search. Caveats: partial: True means 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 with partial: True mean "search failed", not "no
results". The cause is in partial_reason, also mirrored into
warnings[] with an "incomplete results: " prefix. Do not treat a
partial response as complete.
count is items in this response (post-pagination), not corpus
totals — use entity_total_matches + config_total_matches.
limit/offset apply per-surface. Flat has_more/next_offset
page the next call (iterate offset = next_offset); per-surface
entity_*/config_* variants show which surface still has results.
For parameters, schema, and worked examples, see ha_get_skill_guide. Examples:
- List sensors in an area: ha_search(domain_filter="sensor", area_filter="Living Room")
- Find a light by name: ha_search("kitchen", domain_filter="light")
- Which automations use an entity: ha_search("light.bed_light")
- Scenes touching a light: ha_search("light.kitchen", search_types=["scene"])
- Narrow the response to the entity bucket: ha_search("kitchen", fields=["entities"])
- 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 |
| ha_bulk_controlC | Control multiple devices with bulk operation support and WebSocket tracking. |
| ha_call_eventA | 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. For triggering automations by name, use
ha_call_service("automation", "trigger"). 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. |
| ha_call_serviceA | Execute Home Assistant services to control entities and trigger automations. This is the universal tool for controlling all Home Assistant entities. Services follow
the pattern domain.service (e.g., light.turn_on, climate.set_temperature). Basic Usage: # Turn on a light
ha_call_service("light", "turn_on", entity_id="light.living_room")
# Set temperature with parameters
ha_call_service("climate", "set_temperature",
entity_id="climate.thermostat", data={"temperature": 22})
# Trigger automation
ha_call_service("automation", "trigger", entity_id="automation.morning_routine")
# Universal controls work with any entity
ha_call_service("homeassistant", "toggle", entity_id="switch.porch_light")
Key behavior: wait (default True): wait for the entity state to change before
returning. Only applies to state-changing services on a single entity. 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). Escape hatches: verbose=True
for the raw changed-state records, or result_fields /
result_attribute_keys for explicit per-record projection (mirrors
ha_get_state). return_response (default False): the service's response data is
returned once, as the top-level service_response key — never nested
inside result, which carries the changed entity states.
For detailed service documentation, use ha_get_skill_guide. Common patterns: Use ha_get_state() to check current values before making changes.
Use ha_search() to find correct entity IDs. WebSocket command escape hatch (advanced):
A few Home Assistant operations are WebSocket-only commands, not
registered services — most notably dismissing a Repairs issue. Pass
ws_command (instead of domain/service) to send one, with its
parameters in data: # Dismiss a repair (get domain/issue_id from ha_get_overview repairs
# or ha_get_system_health include="repairs")
ha_call_service(ws_command="repairs/ignore_issue",
data={"domain": "sun", "issue_id": "abc", "ignore": True})
Only one-shot request/response commands are supported; streaming/two-phase
and service-invoking commands are rejected, and the other service
parameters (entity_id, return_response, etc.) don't apply. |
| ha_get_operation_statusA | Get the status of one or more device operations with real-time WebSocket verification. Pass a single operation_id string to check one operation, or a list of IDs
to check multiple operations at once (bulk status). The timeout_seconds wait window bounds both modes. Bulk checks poll
all operations concurrently under one shared window and report
per-item failures inside detailed_results instead of aborting the
batch. Use this to track operations initiated by ha_bulk_control or ha_call_service.
For current entity states, use ha_get_state instead. |
| ha_list_servicesA | List available Home Assistant services with optional pagination and detail control. Discovers services/actions that can be called via ha_call_service.
Use domain or query filters to narrow results. Defaults to summary mode
(name + description only) to keep responses compact. |
| ha_get_system_healthA | 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. The result also carries an ha_mcp_update object —
{current, latest, update_available} — reporting whether a newer
ha-mcp release is available (from PyPI for pip/Docker, or the Supervisor
add-on store for the add-on), so you can proactively tell the user to
upgrade. Present on every install type including the HA add-on (so a user
who missed the Supervisor's update prompt still hears about it); omitted
only for the unknown version and when HA_MCP_DISABLE_UPDATE_CHECK
is set. Parameters: include: Optional comma-separated list of additional data to include. "repairs": Repair items from Settings > System > Repairs (active only by default; pass include_dismissed_repairs=True for all). To dismiss/ignore a repair, call ha_call_service(ws_command="repairs/ignore_issue", data={"domain": <domain>, "issue_id": <issue_id>, "ignore": true}). "zha_network": ZHA Zigbee devices with radio signal summary (name, LQI, RSSI) "zha_network_full": ZHA Zigbee devices with all device details (can be large on 100+ device networks; prefer "zha_network" for summary) "zwave_network": Z-Wave JS network status and node summary (status, security, routing) "thread_network": Thread/OpenThread Border Router (OTBR) summary — per border-router channel, extended_pan_id, and border_agent_id (integration-presence + radio-network view, not per-node Thread health) "matter_network": Matter integration presence summary — config_entry_id, state, and title (per-node health is exposed separately via Matter node diagnostics, not here) "themes": Installed theme names and defaults (sorted list of theme names, count, default_theme, default_dark_theme) "diagnostics": Per-integration diagnostics dump — integration-defined JSON
(commonly includes redacted config, device list, state snapshots; exact
top-level keys vary by integration). REQUIRES config_entry_id. The
canonical artifact users grab via Settings → Devices & Services →
[integration] → ⋯ → Download diagnostics. Use this 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. "config_check": Validate HA configuration via POST /config/core/check_config
(the pre-restart safety check; ha_restart runs it automatically). Returns
{result: valid|invalid, is_valid, errors}; read-only/idempotent, takes no args. "dead_entities": Surface orphaned/stale entity-registry entries by diffing
the registry against the state machine and the live config-entries set.
Returns confidence-tiered buckets — config_entry_orphans (owning
integration instance gone; definitively dead) and stale_restored (HA
restored the entity from the registry on startup but the loaded integration
no longer provides it). Each item carries entity_id + platform so a client
can propose cleanup with ha_remove_entity. Deliberately excludes
unknown-state entities and merely-offline devices to keep false positives
low. Read-only; takes no args. Example: include="repairs,zha_network,zwave_network,config_check" Example: include="diagnostics", config_entry_id="abc123..."
include_dismissed_repairs: Include user-dismissed/ignored repairs (default: False). Only meaningful when "repairs" is in include. config_entry_id: Required when include contains diagnostics. The config
entry ID of the integration (find via ha_get_integration). device_id: Optional. When set with include=diagnostics, 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. diagnostics_fields: Optional list of 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_truncate_at_bytes: Optional 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). Only applies when include contains diagnostics.
Recommended starting point: 20000 bytes. diagnostics_data_path: Optional 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
when include contains diagnostics. diagnostics_data_offset / diagnostics_data_limit: Pagination on
list-valued diagnostics_data_path results. When data_limit
is set and the resolved path is a list, data becomes
{"path", "items", "offset", "limit", "total", "has_more"}. Only
applies when include contains diagnostics. Example workflow (walk a list-valued sub-tree one page at a time;
the exact data_path varies by integration version):
ha_get_system_health(include="diagnostics", config_entry_id="abc", diagnostics_data_path="<list-valued path>", diagnostics_data_limit=10)
→ inspect the page envelope's total / has_more → repeat
with diagnostics_data_offset=10 for the next slice.
|
| ha_reload_coreA | Reload Home Assistant configuration without full restart. This tool reloads specific configuration components, allowing changes
to take effect without restarting the entire Home Assistant instance.
This is much faster than a full restart. Parameters: target: What to reload. Options: "all": Reload all reloadable components "automations": Reload automation configurations "scripts": Reload script configurations "scenes": Reload scene configurations "groups": Reload group configurations "input_booleans": Reload input_boolean helpers "input_numbers": Reload input_number helpers "input_texts": Reload input_text helpers "input_selects": Reload input_select helpers "input_datetimes": Reload input_datetime helpers "input_buttons": Reload input_button helpers "timers": Reload timer helpers "templates": Reload template sensors/entities "persons": Reload person configurations "zones": Reload zone configurations "core": Reload core configuration (customize, packages) "themes": Reload frontend themes
entry_id: 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 its "all" default);
combining it with an explicit target is a validation error. Find the
id via ha_get_integration.
Example Usage: # Reload just automations after editing
ha_reload_core(target="automations")
# Reload all configurations
ha_reload_core(target="all")
# Reload input helpers after adding new ones
ha_reload_core(target="input_booleans")
When to Use: After editing automation/script YAML files After adding new input helpers via YAML After modifying customize.yaml After theme changes
|
| ha_restartA | Restart Home Assistant. WARNING: This will restart the entire Home Assistant instance!
All automations will be temporarily unavailable during restart.
The restart typically takes 1-5 minutes depending on your setup. Parameters: Best Practices: Config is validated automatically before the restart proceeds; to
pre-check, call ha_get_system_health(include="config_check") Notify users before restarting (if applicable) Schedule restarts during low-activity periods
Example Usage: # Optional pre-check (ha_restart also validates config automatically)
health = ha_get_system_health(include="config_check")
if health["config_check"]["is_valid"]:
# Restart with confirmation
result = ha_restart(confirm=True)
Alternative: For configuration changes, consider using ha_reload_core()
instead, which reloads specific components without a full restart. |
| ha_manage_themeA | 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). 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")
|
| ha_get_todoA | Get todo lists or items - list all todo lists or get items from a specific list. Without an entity_id: Lists all todo list entities in Home Assistant.
With an entity_id: Gets items from that specific todo list, optionally filtered by status. LISTING TODO LISTS (entity_id omitted):
Returns all entities in the 'todo' domain, including shopping lists
and any other todo-type integrations. Each todo list includes: entity_id: The unique identifier (e.g., 'todo.shopping_list') friendly_name: Human-readable name state: Number of incomplete items or current status
GETTING TODO ITEMS (entity_id provided):
Retrieves items from the specified todo list. Status filter values: needs_action: Items that still need to be done completed: Items that have been marked as done None (default): Returns all items regardless of status
Item properties: uid: Unique identifier for the item summary: The item text/description status: Current status (needs_action or completed) description: Optional detailed description due: Optional due date (if supported)
EXAMPLES: List all todo lists: ha_get_todo() Get all items: ha_get_todo("todo.shopping_list") Get incomplete items: ha_get_todo("todo.shopping_list", status="needs_action") Get completed items: ha_get_todo("todo.shopping_list", status="completed")
USE CASES: "What todo lists do I have?" "Show me my shopping list" "What's on my todo list?" "Show completed items"
|
| ha_remove_todo_itemA | Remove an item from a Home Assistant todo list. Permanently deletes an item from the specified todo list. IDENTIFYING ITEMS: EXAMPLES: Remove by name: ha_remove_todo_item("todo.shopping_list", "Buy milk") Remove by UID: ha_remove_todo_item("todo.shopping_list", "abc123-uid")
USE CASES: WARNING: This permanently removes the item. To mark as completed instead,
use ha_set_todo_item() with status="completed". |
| ha_set_todo_itemA | Create or update a todo item in Home Assistant. WITHOUT item parameter (create mode):
Creates a new item. summary is required. WITH item parameter (update mode):
Updates an existing item identified by UID or exact name.
At least one update field (rename, status, description, due_date, due_datetime) is required. EXAMPLES: Add item: ha_set_todo_item("todo.shopping_list", summary="Buy milk") Add with description: ha_set_todo_item("todo.shopping_list", summary="Buy milk", description="2% organic") Add with due date: 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") Update due date: ha_set_todo_item("todo.tasks", item="Pay bills", due_date="2024-12-31") Reopen item: ha_set_todo_item("todo.tasks", item="Task to redo", status="needs_action")
NOTE: Not all todo integrations support all features (description, due dates).
The Shopping List integration only supports summary. |
| ha_get_automation_tracesA | Retrieve execution traces for automations and scripts to debug issues. Traces show what happened during automation/script runs: What triggered the automation Which conditions passed or failed What actions were executed Any errors that occurred Variable values during execution
USAGE MODES: List recent traces (omit run_id):
ha_get_automation_traces("automation.motion_light")
Returns a summary of recent execution runs with timestamps, triggers, and status.
Use offset to page deeper when has_more is true, or order="oldest" to
start from the earliest stored trace instead of the most recent. Get detailed trace (provide run_id):
ha_get_automation_traces("automation.motion_light", run_id="1705312800.123456")
Returns full execution details including trigger info, condition results,
action trace with timing, and context variables. Get detailed trace with logbook (provide run_id and detailed=True):
ha_get_automation_traces("automation.motion_light", run_id="1705312800.123456", detailed=True)
Returns the formatted trace plus logbook entries and context metadata.
Useful when the standard trace summary doesn't reveal enough for debugging.
Note: script-style action paths (sequence/, numeric) are always matched
regardless of this flag. Get full variables without deduplication (provide run_id and deduplicate=False):
ha_get_automation_traces("automation.motion_light", run_id="1705312800.123456", deduplicate=False)
Returns the formatted trace with full variables at every action step.
DEBUGGING EXAMPLES: Automation not triggering: Automation runs but conditions fail: Unexpected behavior in actions: Get detailed trace to see action_trace Shows each action step with result and any errors For 'choose' actions, shows which branch was taken
Template debugging: NOTES: Traces are stored for a limited time by Home Assistant Works for both automations and scripts (use full entity_id) The 'state' field shows: 'stopped' (completed), 'running', or error state
|
| ha_manage_updatesA | Manage Home Assistant updates -- list, read details, batch install, skip, or un-skip. Covers Core, OS, supervisor, apps (add-ons), device firmware, and HACS
update entities. 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. EXAMPLES: List all updates: ha_manage_updates() 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"])
RETURNS (action='list'): updates_available, updates, categories, and
ha_mcp_update -- this MCP server's own update status {current, latest,
update_available}, so a newer ha-mcp release can be flagged. RETURNS (action='get'): update details, release notes; with
include_release_notes=True on Core also breaking_changes.entries[],
multi_version_release_notes[], and installed_integrations. |
| ha_get_logsA | Get Home Assistant logs from various sources. Sources: "logbook" (default): Entity state change history with pagination "system": Structured system log entries (errors, warnings) via system_log/list "error_log": Raw home-assistant.log text "supervisor": Add-on container logs (requires slug = add-on slug) "system_service": HA-Supervisor-managed system service logs (requires
slug ∈ {supervisor, host, core, dns, audio, cli, multicast, observer}) "logger": Effective log level per integration via logger/log_info (confirms logger.set_level changes took effect)
Shared params: limit, search (keyword filter on entries/lines; matches integration domain for source='logger')
Order: order='newest' (default) returns most-recent first; order='oldest' returns chronological-first. Applies to all time-ordered sources (logbook, system, error_log, supervisor, system_service); ignored for source='logger'. For raw-text sources (error_log, supervisor, system_service) it sets the read direction of the most-recent window.
Logbook params: hours_back, entity_id, end_time, offset, compact (default True — strips attribute dicts to save context)
System/error_log params: level (ERROR, WARNING, INFO, DEBUG)
Supervisor params: slug = add-on slug, e.g. "core_mosquitto" (use
ha_get_addon() to list installed slugs)
System-service params: slug = service name. The slug "supervisor"
here means the Supervisor service's own logs, NOT an add-on with
that name — the source param disambiguates. |
| ha_eval_templateA | Evaluate Jinja2 templates using Home Assistant's template engine. This tool allows testing and debugging of Jinja2 template expressions that are commonly used in
Home Assistant automations, scripts, and configurations. It provides real-time evaluation with
access to all Home Assistant states, functions, and template variables. When NOT to use this for automation/script logic:
Templates have legitimate uses (notification bodies, dynamic data.* values,
debugging existing templates), but condition: / trigger: positions and
action service names are better expressed as native HA constructs:
native constructs are schema-validated at config load and surface
structural errors loudly, whereas equivalent template logic only errors
at runtime — and a template that renders a non-truthy value is silently
treated as false.
Prefer: condition: numeric_state over {{ states('x') | float > N }}
condition: state over {{ is_state(...) }}
condition: time / condition: sun over now().hour / is_state('sun.sun', ...)
Native for: field on state/numeric_state triggers and state conditions over
{{ now() - X.last_changed > timedelta(...) }} duration math choose action over templated service: / action: strings
See ha_get_skill_guide (best-practices skill) for the full anti-pattern list.
When to use (reach for this tool, don't compute it yourself):
Any one-shot question whose answer is DERIVED from current HA state — an
average/sum/min/max across sensors, a count of entities matching a
condition, a boolean comparison, or a rendered message with live values.
One render call beats fetching N states and doing the math yourself, and
it is the canonical way to test a template before embedding it. This is
for one-shot answers and template testing only — NOT for putting templates
into automation logic; for condition: / trigger: positions native
constructs win. "average temperature across the bedroom sensors"
-> {{ ([states('sensor.a'), states('sensor.b')] | map('float', 0) | sum) / 2 }} "how many lights are on"
-> {{ states.light | selectattr('state', 'eq', 'on') | list | count }}
NOT for a plain single-entity value ("what's the state of X") — that is
ha_get_state / ha_search; rendering {{ states('X') }} there is over-use.
Parameters: template: The Jinja2 template string to evaluate timeout: Maximum evaluation time in seconds (default: 3) report_errors: Whether to return detailed error information (default: True)
Common Template Functions: State Access: {{ states('sensor.temperature') }} # Get entity state value
{{ states.sensor.temperature.state }} # Alternative syntax
{{ state_attr('light.bedroom', 'brightness') }} # Get entity attribute
{{ is_state('light.living_room', 'on') }} # Check if entity has specific state
Numeric Operations: {{ states('sensor.temperature') | float(0) }} # Convert to float with default
{{ states('sensor.humidity') | int(0) }} # Convert to integer with default
{{ (states('sensor.temp') | float(0) + 5) | round(1) }} # Math operations
Time and Date: {{ now() }} # Current datetime
{{ now().strftime('%H:%M:%S') }} # Format current time
{{ as_timestamp(now()) }} # Convert to Unix timestamp
{{ now().hour }} # Current hour (0-23)
{{ now().weekday() }} # Day of week (0=Monday)
Conditional Logic (for display strings — not for condition: positions): {{ 'Day' if now().hour < 18 else 'Night' }} # Ternary operator
{% if is_state('alarm_control_panel.home', 'armed_away') %}
Alarm is armed
{% else %}
Alarm is disarmed
{% endif %}
Lists and Loops: {% for entity in states.light %}
{{ entity.entity_id }}: {{ entity.state }}
{% endfor %}
{{ states.light | selectattr('state', 'eq', 'on') | list | count }} # Count on lights
String Operations: {{ states('sensor.weather') | title }} # Title case
{{ 'Hello ' + states('input_text.name') }} # String concatenation
{{ states('sensor.data') | regex_replace('pattern', 'replacement') }}
Device and Area Functions: {{ device_entities('device_id_here') }} # Get entities for device
{{ area_entities('living_room') }} # Get entities in area
{{ device_id('light.bedroom') }} # Get device ID for entity
Common Use Cases (legitimate template positions): Dynamic Service Data: # Dynamic brightness based on time
{{ 255 if now().hour < 22 else 50 }}
# Message with current values
"Temperature is {{ states('sensor.temp') }}°C, humidity {{ states('sensor.humidity') }}%"
Examples: Test basic state access: ha_eval_template("{{ states('light.living_room') }}")
Test a string expression (e.g. for a notification body): ha_eval_template("{{ 'Day' if now().hour < 18 else 'Night' }}")
Test mathematical operations: ha_eval_template("{{ (states('sensor.temperature') | float(0) + 5) | round(1) }}")
Test entity counting: ha_eval_template("{{ states.light | selectattr('state', 'eq', 'on') | list | count }}")
IMPORTANT NOTES: Templates have access to all current Home Assistant states and attributes Use this tool to test templates before using them in automations or scripts Template evaluation respects Home Assistant's security model and timeouts Complex templates may affect Home Assistant performance - keep them efficient Use default values (e.g., | float(0)) to handle missing or invalid states
For template documentation: https://www.home-assistant.io/docs/configuration/templating/ |
| ha_get_entity_exposureA | Get entity exposure settings - list all or get settings for a specific entity. Without an entity_id: Lists all entities and their exposure status to
voice assistants (Alexa, Google Assistant, Assist). With an entity_id: Returns which voice assistants the specific entity
is exposed to. EXAMPLES: List all exposures: ha_get_entity_exposure() Filter by assistant: ha_get_entity_exposure(assistant="cloud.alexa") Get specific entity: ha_get_entity_exposure(entity_id="light.living_room")
RETURNS (when listing): RETURNS (when getting specific entity): 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. |
| ha_manage_pipelineA | 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,
and action='set_preferred' to choose the preferred pipeline. EXAMPLES: List pipelines: ha_manage_pipeline(action="list") Get one pipeline: ha_manage_pipeline(action="get", pipeline_id="preferred") Create by cloning preferred: ha_manage_pipeline(
action="create",
name="Local Assist",
conversation_engine="conversation.local_llm",
) Create by cloning a specific pipeline: ha_manage_pipeline(
action="create",
base_pipeline_id="preferred",
name="Local Assist",
conversation_engine="conversation.local_llm",
) Update conversation agent and clear TTS voice: ha_manage_pipeline(
action="update",
pipeline_id="preferred",
conversation_engine="conversation.local_llm",
tts_voice="",
) Set preferred: ha_manage_pipeline(
action="set_preferred",
pipeline_id="preferred",
)
Empty string clears nullable STT/TTS/wake-word fields. Non-nullable
fields such as name, language, conversation_language, and
conversation_engine must be omitted or non-empty. |
| ha_get_zoneA | 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. ZONE PROPERTIES: EXAMPLES: NOTE: 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. |
| ha_remove_zoneA | Remove a Home Assistant zone. EXAMPLES: 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. |
| ha_set_zoneA | 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). EXAMPLES: Create: ha_set_zone(name="Office", latitude=40.7128, longitude=-74.0060, radius=150, icon="mdi:briefcase") Update name: ha_set_zone(zone_id="abc123", name="New Office") Update radius: ha_set_zone(zone_id="abc123", radius=200) Update location: ha_set_zone(zone_id="abc123", latitude=40.7128, longitude=-74.0060)
Note: The 'home' zone is typically defined in YAML and cannot be modified via this API. |
| ha_manage_backupA | Manage Home Assistant backups — both full HA snapshots AND per-edit auto-backups. Pick the scope first, then the action. Wrong scope routes through the wrong code path: scope | action | What it does | snapshot
| create
| Create a full HA tarball (config + addons, no DB by default). Can take a while on a large instance; progress heartbeats are sent while waiting. | snapshot
| list
| List full HA tarball snapshots (id, name, date, size). Read-only — use to discover a backup_id or confirm a backup landed. | snapshot
| restore
| Restore a full HA tarball. Restarts HA. Last-resort recovery. | snapshot
| delete
| Delete one full HA tarball by backup_id (confirm=True required). Disabled by default (enable_snapshot_delete setting) and layered with guards even when enabled — see below. | edits
| create
| On-demand snapshot of one entity (domain + entity_id required). Use before the user manually edits in the HA UI. Same handler path the decorator takes on writes; bypasses the enable_auto_backup toggle. | edits
| list
| List per-entity auto-backups (lightweight). Filter by domain and/or entity_id. | edits
| view
| Read one auto-backup file by name; returns YAML and parsed config. | edits
| diff
| Compare one auto-backup against the entity's current config. RFC 6902 JSON-Patch + add/remove/replace counts; bounded output. Read-only — fetches the live config, makes no changes. | edits
| restore
| Re-apply one auto-backup. Creates a fresh safety snapshot first. No HA restart. | edits
| delete
| Delete one auto-backup by backup_name, or bulk-delete by filter. |
When to use which scope: Use scope="edits" to undo a recent automation/script/scene/dashboard/helper edit by the agent. Lightweight, fast, no restart. Use scope="snapshot" only for system-wide recovery (botched add-on update, mass config corruption, etc.).
scope="snapshot" backup-hint:
Run before operations that CANNOT be undone (e.g., deleting devices). If the current definition was fetched or can be fetched, this tool is usually not needed.
(snapshot, delete) is off by default and layered even when enabled: a human must
set enable_snapshot_delete=true (env var, web settings UI, or add-on Supervisor
options) — an agent cannot turn this on itself. When enabled, a delete call is still
refused if: the target is a scheduled/automatic backup; it's younger than
snapshot_delete_min_age_days (default 7, 0 disables the floor); or it's the single
newest snapshot remaining. These guarantee at least one recovery point always
survives an agent's own mistakes.
enable_auto_backup and scope="edits": the automatic-on-write capture (every wrapped tool call) is gated by enable_auto_backup=true — if the listing is empty, check the toggle (web settings UI or ENABLE_AUTO_BACKUP=true env var). The explicit (edits, create) action bypasses the toggle since the request is explicit; list / view / restore / delete operate on whatever's already on disk regardless of the toggle's current state.
Examples: Snapshot before risky op: ha_manage_backup(scope="snapshot", action="create", name="Before_Big_Change") List snapshots (to discover a backup_id or confirm one landed): ha_manage_backup(scope="snapshot", action="list") Restore full snapshot: ha_manage_backup(scope="snapshot", action="restore", backup_id="dd7550ed") Delete an old snapshot (requires enable_snapshot_delete=true): ha_manage_backup(scope="snapshot", action="delete", backup_id="dd7550ed", confirm=True) On-demand entity snapshot before a manual UI edit: ha_manage_backup(scope="edits", action="create", domain="helper_input_boolean", entity_id="kitchen_lights_active") List recent auto-backups for one automation: ha_manage_backup(scope="edits", action="list", domain="automation", entity_id="kitchen_lights") View an auto-backup: ha_manage_backup(scope="edits", action="view", backup_name="automation.kitchen_lights.20260521_153000.yaml") Diff an auto-backup vs current state: ha_manage_backup(scope="edits", action="diff", backup_name="automation.kitchen_lights.20260521_153000.yaml") Restore an auto-backup: ha_manage_backup(scope="edits", action="restore", backup_name="automation.kitchen_lights.20260521_153000.yaml") Delete one auto-backup: ha_manage_backup(scope="edits", action="delete", backup_name="...") Bulk-delete old auto-backups: ha_manage_backup(scope="edits", action="delete", older_than_days=30)
|
| ha_get_skill_guideA | Get bundled Home Assistant best-practice skill guides. No skill bundles are currently available on this server — the skills directory is missing, empty, or all SKILL.md files failed to parse. Calls return an empty listing; 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 |