Skip to main content
Glama
homeassistant-ai

Home Assistant MCP Server

Official

Manage Add-on

ha_manage_addon
Destructive

Install, 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'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

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
bodyNoProxy mode only. Request body for POST/PUT/PATCH — or, with websocket=True, the initial WebSocket message. Pass a JSON object or JSON string.
bootNoConfig mode: Boot strategy — 'auto' (start with HA) or 'manual'.
pathNoProxy mode: API path relative to the add-on root (e.g., '/flows', '/api/events', '/api/stats'). Required for proxy mode; mutually exclusive with config parameters.
portNoProxy mode only. Connect to this port instead of the Ingress port. Use ha_get_addon(slug='...') to find available ports.
slugNoAdd-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.
debugNoProxy mode only. Include diagnostic info (request URL, headers sent, response headers). Default: false.
limitNoProxy mode only. HTTP: return at most this many items from a JSON array response.
actionNoLifecycle 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.
methodNoProxy mode only. HTTP method: GET, POST, PUT, DELETE, PATCH. Defaults to GET.GET
offsetNoProxy mode only. HTTP: skip this many items in a JSON array response. Default: 0.
networkNoConfig mode: Host port mappings (e.g., {'5800/tcp': 8081}).
optionsNoConfig mode: Add-on configuration values (the 'Configuration' tab in the UI).
watchdogNoConfig mode: Enable or disable Supervisor watchdog (auto-restart on crash).
summarizeNoProxy mode only. WebSocket: when True (default), collapse runs of non-signal messages (typically YAML config dumps) into short elision markers. Set to False to return the raw stream.
websocketNoProxy mode only. Use WebSocket instead of HTTP — for an 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.
repositoryNoStore-repository mode only (action='add_repository' or 'remove_repository'). For add_repository: the repository URL (e.g., 'https://github.com/balloob/home-assistant-addons'). For remove_repository: the repository slug (e.g., '0f1cc410', as shown in ha_get_addon(source='available')). Required for those actions; ignored otherwise.
array_patchNoArray-patch mode: atomically GET a JSON array endpoint, apply ordered ops, then POST the mutated array back. Requires 'path'; mutually exclusive with body / websocket / offset / limit and config params. See the docstring Examples and ha_get_skill_guide for op shapes.
auto_updateNoConfig mode: Enable or disable automatic updates for this add-on.
message_limitNoProxy mode only. WebSocket: cap on messages collected from the wire, bounded by an internal safety ceiling. None = collect up to the ceiling. Lower to save tokens on noisy streams (e.g., message_limit=50 for a quick health check).
message_offsetNoProxy 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_closeNoProxy 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_headersNoProxy/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_transformNoProxy mode only. Sandboxed Python expression that post-processes the response. Variable `response` is exposed — a list[dict | str] for WebSocket (parsed JSON or raw text), or dict/list/str for HTTP (parsed body). Supports in-place mutation (response.append(...)) or reassignment (response = [...]). Example: response = [m for m in response if 'ERROR' in str(m)]. Post-processing only — does not provide optimistic-locking write semantics.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Behavior5/5

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

The description fully discloses behavioral traits: it can be destructive (uninstall, remove repository), requires HA OS or Supervised, and provides warnings about boot mode failures. It does not contradict annotations (destructiveHint=true, readOnlyHint=false).

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

Conciseness3/5

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

The description is very long and includes extensive details (e.g., ESPHome API specifics, array-patch examples). While well-structured with sections, it lacks conciseness and could be trimmed to improve readability without losing essential guidance.

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

Completeness5/5

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

Given the tool's complexity (23 parameters, multiple modes, output schema), the description is thorough. It covers edge cases, error scenarios, and provides comprehensive examples, making it fully complete for correct invocation.

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

Parameters5/5

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

Despite 100% schema coverage, the description adds significant value by explaining parameter relationships across modes, providing detailed examples, and clarifying nuances like the merge behavior of options. It far exceeds the baseline level of 3.

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

Purpose5/5

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

The description clearly states the tool manages a Home Assistant add-on with five distinct operating modes (lifecycle, store-repository, config, proxy, array-patch). It uses specific verbs and resources, and distinguishes its purpose from sibling tools like ha_get_addon.

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

Usage Guidelines5/5

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

The description explicitly explains when to use each mode with conditions, prerequisites (e.g., repository must be registered), and exclusions (e.g., store-repository does not use slug). It provides clear guidance on when not to use certain options, such as boot mode failure when locked.

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

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/homeassistant-ai/ha-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server