Manage Add-on
ha_manage_addonInstall, configure, and manage Home Assistant add-ons, control their lifecycle, and proxy to their internal APIs or WebSocket endpoints.
Instructions
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'sconfigurationfield is the YAML filename to pass below.WebSocket
path="/ws"with body{"command": "<cmd>", "message_id": "1", "args": {...}}. The server sends aserver_infomessage first, then one reply permessage_id. Wire-confirmed commands:devices/get_config{configuration}→ raw YAML (in the reply'sresult);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
/wschannel stays open, so for a one-shot read or a bounded log capture passwait_for_close=Falsewithmessage_limit(andmessage_offsetto skip the server_info / config-banner preamble). Reach the dashboard through Ingress — omitport; directport=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=Truecollapses long runs of non-signal messages into short elision markers; INFO/WARNING/ERROR/exit lines always pass through. Pagination viamessage_offset/message_limitworks on the raw collected list before summarize runs.python_transformapplies a sandboxed Python expression as a final post-processing step in both HTTP and WebSocket modes. The variableresponseis 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
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | Proxy mode only. Request body for POST/PUT/PATCH — or, with websocket=True, the initial WebSocket message. Pass a JSON object or JSON string. | |
| boot | No | Config mode: Boot strategy — 'auto' (start with HA) or 'manual'. | |
| path | No | Proxy mode: API path relative to the add-on root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode; mutually exclusive with config parameters. | |
| port | No | Proxy mode only. Connect to this port instead of the Ingress port. Use ha_get_addon(slug='...') to find available ports. | |
| slug | No | Add-on slug (e.g., '<prefix>_nodered', '<prefix>_frigate'). Slug prefixes vary by add-on repository — call ha_get_addon() to discover the actual installed slug. Required for every mode except the store-repository actions (action='add_repository'/'remove_repository'), which use 'repository' instead and take no slug. | |
| debug | No | Proxy mode only. Include diagnostic info (request URL, headers sent, response headers). Default: false. | |
| limit | No | Proxy mode only. HTTP: return at most this many items from a JSON array response. | |
| action | No | Lifecycle mode: run a Supervisor add-on action. One of 'install', 'uninstall', 'start', 'stop', 'restart', 'rebuild', 'update'. 'install'/'update' require the add-on's repository to be registered (it appears in ha_get_addon(source='available')). Store-repository mode: 'add_repository' / 'remove_repository' register or unregister a custom add-on store repository — these use the 'repository' param instead of 'slug'. Mutually exclusive with path / config parameters / array_patch. HA OS / Supervised only. | |
| method | No | Proxy mode only. HTTP method: GET, POST, PUT, DELETE, PATCH. Defaults to GET. | GET |
| offset | No | Proxy mode only. HTTP: skip this many items in a JSON array response. Default: 0. | |
| network | No | Config mode: Host port mappings (e.g., {'5800/tcp': 8081}). | |
| options | No | Config mode: Add-on configuration values (the 'Configuration' tab in the UI). | |
| watchdog | No | Config mode: Enable or disable Supervisor watchdog (auto-restart on crash). | |
| summarize | No | Proxy mode only. WebSocket: when True (default), collapse runs of non-signal messages (typically YAML config dumps) into short elision markers. Set to False to return the raw stream. | |
| websocket | No | Proxy mode only. Use WebSocket instead of HTTP — for an add-on's WebSocket API (e.g. the ESPHome dashboard's '/ws' command channel; see the docstring's ESPHome section). Sends 'body' as the initial message, collects responses. Default: false. | |
| repository | No | Store-repository mode only (action='add_repository' or 'remove_repository'). For add_repository: the repository URL (e.g., 'https://github.com/balloob/home-assistant-addons'). For remove_repository: the repository slug (e.g., '0f1cc410', as shown in ha_get_addon(source='available')). Required for those actions; ignored otherwise. | |
| array_patch | No | Array-patch mode: atomically GET a JSON array endpoint, apply ordered ops, then POST the mutated array back. Requires 'path'; mutually exclusive with body / websocket / offset / limit and config params. See the docstring Examples and ha_get_skill_guide for op shapes. | |
| auto_update | No | Config mode: Enable or disable automatic updates for this add-on. | |
| message_limit | No | Proxy mode only. WebSocket: cap on messages collected from the wire, bounded by an internal safety ceiling. None = collect up to the ceiling. Lower to save tokens on noisy streams (e.g., message_limit=50 for a quick health check). | |
| message_offset | No | Proxy mode only. WebSocket: drop this many messages from the start of the collected list before returning. Useful for paginating past known-noisy headers. Default: 0. | |
| wait_for_close | No | Proxy mode only. WebSocket: True: wait for the server to close the stream (run-to-completion ops like an ESPHome compile/validate). False: return after the first response batch — use for a one-shot command/response or a bounded log capture on a channel that stays open (e.g. ESPHome '/ws'). Default: true. | |
| request_headers | No | Proxy/array-patch mode: extra HTTP headers to send to the addon API. Useful for addon-specific requirements such as Node-RED's `Node-RED-Deployment-Type: full`. The proxy's internal framing (`X-Ingress-Path`, `X-Hass-Source`, `Cookie`, `Content-Type`) is layered on top, so caller-supplied values for those keys are overridden. Not valid in config or websocket mode. | |
| python_transform | No | Proxy mode only. Sandboxed Python expression that post-processes the response. Variable `response` is exposed — a list[dict | str] for WebSocket (parsed JSON or raw text), or dict/list/str for HTTP (parsed body). Supports in-place mutation (response.append(...)) or reassignment (response = [...]). Example: response = [m for m in response if 'ERROR' in str(m)]. Post-processing only — does not provide optimistic-locking write semantics. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||