Skip to main content
Glama
homeassistant-ai

Home Assistant MCP Server

Official

Manage App (add-on)

ha_manage_app
Destructive

Manage Home Assistant add-ons: install, start, stop, update, configure settings, add repositories, or call internal APIs via HTTP/WebSocket.

Instructions

Manage a Home Assistant App (formerly known as an add-on, and this tool as ha_manage_addon) — 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_app(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).

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), which is what summarize is for. INFO/WARNING/ERROR/exit lines always pass through it, and pagination via message_offset / message_limit works on the raw collected list before summarize runs.

  • python_transform runs after slicing and summarize, and before the response size cap, so it can narrow an oversized response back under the limit. What response binds to and what may be done to it is on the parameter itself. Only the interaction is here: undecodable WebSocket frames arrive as ANSI-stripped strings, and elision markers as {"elided": N, "note": "..."} dicts when summarize ran.

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_app(slug="...", action="install")

  • Start an add-on: ha_manage_app(slug="...", action="start")

  • Add a store repository: ha_manage_app(action="add_repository", repository="https://github.com/balloob/home-assistant-addons")

  • Remove a store repository: ha_manage_app(action="remove_repository", repository="0f1cc410")

  • Set add-on option: ha_manage_app(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_app(slug="...", auto_update=False)

  • Change host port: ha_manage_app(slug="...", network={"5800/tcp": 8082})

  • Set boot mode: ha_manage_app(slug="...", boot="manual")

  • Call HTTP API: ha_manage_app(slug="...", path="/api/events")

  • Direct port: ha_manage_app(slug="...", path="/flows", port=1880)

  • ESPHome list devices (HTTP): ha_manage_app(slug="_esphome", path="/devices")

  • ESPHome read a device's YAML (WS one-shot): ha_manage_app(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_app(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_app(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_app(slug="...", path="/flows", python_transform="response = [f['id'] for f in response]")

  • Array-patch (Node-RED, rename a node): ha_manage_app( 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_app( 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_app(slug="...", path="/api/state", request_headers={"Accept": "text/plain"})

manage app apps addon add-on configure settings options port network boot watchdog auto_update supervisor ingress proxy websocket api rest esphome nodered node-red frigate mosquitto mqtt zigbee2mqtt zigbee z-wave zwave appdaemon hacs studio code server file editor terminal ssh samba grafana influxdb deconz motioneye compile validate upload deploy firmware ota flash yaml device logs flows events stats

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_app(slug='...') to find available ports.
slugNoAdd-on slug (e.g., '<prefix>_nodered', '<prefix>_frigate'). Slug prefixes vary by add-on repository — call ha_get_app() to discover the actual installed slug. Required for every mode except the store-repository actions (action='add_repository'/'remove_repository'), which use 'repository' instead and take no slug.
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_app(source='available')). Store-repository mode: 'add_repository' / 'remove_repository' register or unregister a custom add-on store repository — these use the 'repository' param instead of 'slug'. Mutually exclusive with path / config parameters / array_patch. HA OS / Supervised only.
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_app(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?

Annotations (readOnlyHint: false, destructiveHint: true, etc.) are already present, and the description adds substantial behavioral detail beyond them: it explains that config mode merges only provided fields (avoiding overwrites), details the ESPHome rewrite (legacy endpoints gone, WebSocket JSON-command API used), describes summarize/pagination behavior, notes idempotency limits (python_transform is post-processing only), and warns about boot failures. It also discloses the atomicity of array-patch and the network requirements for direct port mode. No contradiction with annotations.

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

Conciseness5/5

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

The description is long but well-structured: it starts with a brief purpose, then uses bold headed modes, bullet points, and code-formatted examples. Every sentence adds unique value—no fluff. It front-loads the main purpose and then systematically covers each mode, edge cases, and response shaping. The length is justified by the tool's complexity, and the organization aids comprehension.

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 23 parameters and multiple modes, the description is comprehensive. It covers all modes (lifecycle, store-repository, config, proxy, array-patch), prerequisites, limitations (HA OS/Supervised only), the ESPHome rewrite details, response shaping options, and includes 15+ concrete examples. It also references companion tools (ha_get_app, ha_get_skill_guide) for additional context. The output schema exists, so return values are not needed in the description, and the description handles that appropriately.

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?

Schema coverage is 100% (every parameter has a description), but the description goes far beyond schema descriptions by explaining parameter interactions and semantic contexts: e.g., `slug` is required for most modes but not store-repository actions; `array_patch` requires `path` and is mutually exclusive with body/websocket; `wait_for_close` and `message_offset` are explained in the context of streaming; `python_transform` scope and variable binding are detailed. The description also adds mode-specific nuances like the `/ws` channel staying open and the need to pass `wait_for_close=False` for one-shot reads.

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 states a specific verb+resource ('Manage a Home Assistant App') and immediately distinguishes multiple modes (lifecycle, config, proxy, array-patch). It clearly differentiates from sibling tools like ha_get_app by noting it handles updates and API calls, while ha_get_app is for discovery (referenced for slugs). The purpose is unambiguous and non-tautological.

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 gives explicit when-to-use guidance for each mode, including mutual exclusivity rules (e.g., 'Lifecycle mode when action is...', 'Store-repository mode... take repository and no slug'). It provides prerequisites (e.g., 'repository must be registered'), alternatives (e.g., 'call ha_get_app() to discover the actual installed slug'), and notes the tool only works on HA OS/Supervised. It also includes a warning about boot mode failures and concrete examples for each mode, making usage boundaries crystal clear.

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