Skip to content

Latest commit

 

History

History
412 lines (338 loc) · 25.5 KB

File metadata and controls

412 lines (338 loc) · 25.5 KB

Run the MCP server inside Home Assistant

The HA-MCP Custom Component (ha_mcp_tools) can run the full ha-mcp server in-process, inside the Home Assistant application, and expose it remotely through a Home Assistant webhook. This is one of the ways to run ha-mcp — and the recommended one. It is a complete, standalone ha-mcp install — a replacement for the add-on, Docker, and uvx/PyPI (stdio) server, not an addition to them. Run only one ha-mcp server; never run the in-process server alongside another install.

The in-process server is one of two config-entry types the component offers. The other is the File & YAML services entry (HA-MCP File & YAML Tools), the privileged file / YAML services. They are complementary and independent — you can add either, or both, under the one integration — see Relationship to the File & YAML services entry below.

Who it's for

  • Home Assistant Container and Home Assistant Core users, who cannot install add-ons (add-ons require the Supervisor). Instead of running ha-mcp in a separate Docker container or over stdio, you run it inside Home Assistant itself.
  • Home Assistant OS / Supervised users who would rather not run a separate add-on. It works on HAOS too — the add-on remains a supported alternative, but you only need one of them; the two are fully independent.

Because it reaches the internet through a Home Assistant webhook, the connect URL works through Nabu Casa remote UI (or any reverse proxy pointing at Home Assistant) with no separate tunnel or port forwarding — the same mechanism the Webhook Proxy add-on uses.

How it works

Once the in-process server config entry exists, it:

  1. Installs the ha-mcp package into Home Assistant at runtime (the first start takes a little longer while pip downloads it — see First start below).
  2. Provisions a dedicated Home Assistant admin token the server uses to reach Home Assistant over loopback.
  3. Runs the server on its own thread so a slow tool call can never stall Home Assistant's event loop.
  4. Registers a Home Assistant webhook that forwards MCP traffic to the server, so it is reachable remotely with the webhook URL as the secret.

The bring-up runs in the background, so it never delays Home Assistant startup.

Requirements

The in-process server requires Home Assistant 2026.6.0 or newer. Older Core releases constrain dependencies to versions that cannot run current ha-mcp servers. On older releases, the component remains available for its File & YAML services entry with an external add-on or Docker server, but its config flow blocks creation of the incompatible in-process server entry.

Setup

  1. Install the component. Install HA-MCP Custom Component from HACS (repository homeassistant-ai/ha-mcp-integration, the component's HACS distribution mirror), or, without HACS, copy the custom_components/ha_mcp_tools directory from this repository into your Home Assistant config/custom_components/ directory (so you end up with config/custom_components/ha_mcp_tools/). Restart Home Assistant.
  2. Add the in-process server entry. Go to Settings → Devices & Services → Add Integration, search for HA-MCP Custom Component, and — on the menu that appears — choose HA-MCP Server, then submit the confirmation. Creating the entry starts the server with the defaults. (If you already have the HA-MCP File & YAML Tools entry, use the same Add Integration flow; the two entries appear together under the one integration tile.)
  3. Copy your connect URL. As soon as the server starts, a notification titled HA-MCP Server confirms it is running and points you to the URL. The connect URL itself is on the entry's Configure screen (Settings → Devices & Services → HA-MCP Custom Component → HA-MCP Server → Configure) and in the Home Assistant log — both admin-only surfaces, because the URL is the credential. The notification deliberately carries no URL: notifications are visible to every signed-in user.
  4. Connect your MCP client to that URL.

To pause the server, disable its config entry (Settings → Devices & Services → HA-MCP Custom Component → HA-MCP Server → ⋮ → Disable); re-enable it to start it again. Removing the entry stops the server and revokes the provisioned token.

Connect URLs

The server is reached through a Home Assistant webhook whose id is your secret (it looks like mcp_ followed by a long random string):

  • Remote (Nabu Casa or any external URL): https://<your-nabu-casa-domain>/api/webhook/<webhook-id>
  • Local network: http://<home-assistant-host>:8123/api/webhook/<webhook-id>

The server is also reachable directly on its own port (default - same model as the add-on), bypassing the webhook, at the secret path (which looks like /private_<random>):

  • Direct LAN access: http://<home-assistant-ip>:9584/private_<random>

Set Network access to 127.0.0.1 to turn direct access off and keep only the webhook and panel paths. All connect URLs — the webhook forms and, whenever direct access is on, the direct URL — are listed on the entry's Configure screen and in the Home Assistant log.

Chat with the toolset from Home Assistant (conversation agents / voice)

While the server is running, its toolset is also registered as a Home Assistant LLM API named after the entry. Any conversation agent — OpenAI, Google Generative AI, Anthropic, Ollama, or any other integration that supports LLM APIs — can select it, with the LLM of your choice, cloud or local:

  1. Add a conversation-agent integration (for a fully local setup: Ollama).
  2. In that agent's settings, under Control Home Assistant, select HA-MCP Server (tool search) (alongside or instead of the built-in Assist API).
  3. Talk to the agent from the Assist chat dialog, the companion apps, or a voice satellite whose pipeline uses that agent — "create an automation that turns off the lights when everyone leaves" now runs through the ha-mcp tools.

Exposure modes

The Conversation-agent tool exposure option picks the shape agents get:

  • Tool search (default): a compact API — the pinned tools directly, plus ha_search_tools (find tools for a task) and ha_call_tool (run one). Keeps the agent's context small; works with modest local models.
  • Full catalog: every exposed tool listed directly with its schema. Better tool selection for large-context models, at ~10× the prompt cost.
  • Both: registers the two APIs side by side — each agent picks its own in the selector. One server serves both; nothing runs twice.

Per-tool exposure

Which tools agents may see is managed per tool from the HA-MCP settings panel (the new LLM API toggle next to enabled/pinned/security-gated). It is deny-by-default for beta tools, developer-mode tools, and the restart / reload / backup family — a hidden tool is simply invisible to agents (absent from the catalog and from search results) while staying fully available to your regular MCP clients. Changes apply on the agent's next message, no restart. A tool disabled globally is off for everything, everywhere; the security gate and Read Only Mode apply to agent calls exactly as to any MCP client.

Notes:

  • No MCP client, external URL, or token is involved: the agent reaches the server over loopback inside Home Assistant.
  • Home Assistant conversation agents cap tool iterations per turn (around ten), so a very complex build may need a follow-up prompt to continue.

Security: the toolset runs with the server's admin access. Selecting it on an agent hands that power to everyone who can talk to that agent, including anyone within earshot of a voice satellite using it. Keep it off pipelines where that is not intended.

To remove the API from every agent's selector entirely, turn off Conversation-agent LLM API in the entry's options.

Settings panel ("HA-MCP" in the sidebar)

While a server entry is running, the integration adds an HA-MCP panel to the Home Assistant sidebar. It opens the server web settings UI (tool enable/disable/pin, feature flags, backups, themes) without needing the loopback URL - the same experience as the add-on "Open Web UI" button.

The panel is admin-only. Opening it establishes a short-lived session for your Home Assistant login, and every request re-checks that the account is still an active administrator. No token or secret ever appears in a URL, and the secret path stays on the loopback side of the proxy.

Independent from the add-on

The in-process server and the Home Assistant MCP Server add-on are completely independent: neither requires the other, and there is nothing to configure between them. The in-process server defaults to port 9584 while the add-on uses 9583, so an existing add-on install does not conflict.

Options

Open Settings → Devices & Services → HA-MCP Custom Component → HA-MCP Server → Configure to change these. Saving the options reloads the server so the changes take effect. (The HA-MCP File & YAML Tools entry has no options — a Configure there just reports that.)

Option Default What it does
Release channel stable stable installs the latest stable release; dev installs the latest development build. Both channels update automatically (a reload or restart, plus a periodic check, install the newest build of the selected channel). See Release channels.
Automatic server updates on When on, the selected channel's newest release is installed automatically (on reload/restart and via a periodic check). When off, the server stays on the version currently installed — new releases are still offered on the server's update entity, and its Install button installs one without turning automatic updates back on. Governs the ha-mcp server package only — component updates still come through HACS. A package override below overrides this.
Server port 9584 Local TCP port the server listens on. 9584 avoids the add-on's 9583 so an existing add-on install does not conflict.
Network access 0.0.0.0 The default matches the add-on: the port is reachable on your LAN with the secret path as the credential. 127.0.0.1 restricts direct access to the Home Assistant machine (the webhook and panel work either way).
Authentication mode none none: the secret webhook URL is the credential. ha_auth: clients sign in with your Home Assistant account. legacy: self-hosted OAuth with a static Client ID + Secret, for OAuth-only clients (Gemini Spark, Copilot CLI). See Security.
ha-mcp package (advanced) empty (tracks the selected release channel) The pip requirement installed at runtime. Leave it empty unless you are testing a pre-release — it accepts any pip requirement string, including a version pin or a GitHub tarball URL. An explicit value overrides the release channel and disables automatic updates (a pin stays put until you clear it); changing it forces a reinstall on the next reload.
Home Assistant URL for the server (advanced) empty (derived from your HA's http config) How the in-process server reaches Home Assistant. Empty derives the loopback URL from your instance's real port and SSL setting (an SSL-enabled HA is reached over https://127.0.0.1 with certificate verification off — the certificate never matches a loopback address). Only set a value when the server must take a different route entirely.
Remote access via webhook on Turn off for local-only mode: the webhook is never registered, so Home Assistant (including Nabu Casa) cannot reach the server at all. Direct port access and the sidebar panel keep working.
Conversation-agent LLM API on Offers the toolset to Home Assistant conversation agents — see Chat with the toolset. Enabling only makes it selectable per agent; turn off to remove it from every agent's selector.
Conversation-agent tool exposure tool_search Shape of the toolset agents get: compact tool-search API (default), the full catalog, or both side by side (choose per agent). See Exposure modes.
External URL (optional) empty Shown as the primary connect URL - for your own domain / reverse proxy (e.g. https://ha.example.com). Opening it should reach your HA login page, and must not contain a port like :8123 (any port breaks remote MCP clients). Empty = Nabu Casa / local automatically.
Custom webhook secret (optional) empty Replaces the random webhook secret in /api/webhook/<secret>. The URL is the credential - use a long, hard-to-guess value.
Custom direct-access path (optional) empty Replaces the random /private_... path on the server port. Same rule: the path is the credential.
Regenerate connect secrets now off One-time action: mints fresh random values for both secrets, immediately invalidating the old connect URLs (and clearing the two overrides).

Release channels

The Release channel option selects which build of the server is installed. Both channels install unpinned and update automatically:

  • stable (default): the latest ha-mcp release from PyPI.
  • dev: the latest development build, published to PyPI as ha-mcp-dev on every change to the project's main branch. Use it to try upcoming fixes, and expect the occasional rough edge.

While Automatic server updates is on (the default), both channels install unpinned: an entry reload or a Home Assistant restart always reinstalls the newest build of the selected channel, and on top of that the component checks PyPI for a newer build every 6 hours and reloads the entry automatically when one is published — so a long-running instance picks up releases without a restart. The reload applies the new server code immediately (component >= 1.0.1 reloads the module cache per worker start); only updates that require newer third-party dependencies still need a Home Assistant core restart. The web settings UI's Restart HA-MCP Server button performs the same entry reload. Each automatic update also raises a notification naming the old and new version, with a link to the release notes. Turn Automatic server updates off to freeze the server on the version currently installed: reloads/restarts keep that exact version until you turn it back on or install a newer build yourself from the update entity (this governs the server package only — component updates still arrive through HACS). Setting the ha-mcp package (advanced) field overrides the channel entirely (pin a version, or install from a URL for pre-release testing) and also disables automatic updates until you clear it.

The server's version is always visible on its update entity, under Settings → Devices & Services → HA-MCP Custom Component → HA-MCP Server (and under Settings → System → Updates whenever an update is available). The entity shows the installed and latest version of the selected channel and links the release notes — the 6-hour PyPI check keeps it populated even with automatic updates off, where its Install button installs the offered version on your schedule. The server (7.x) and the component (1.x) are versioned independently: this entity and HACS each own one of the two numbers.

Switching channels reinstalls the server from the other channel on the next reload. ha-mcp and ha-mcp-dev share the same import package, so the previous channel's package is uninstalled first — only one is ever installed at a time.

If the installed server needs a newer version of the custom component than the one you have (HACS can deliver a server build before you update the component), a repair issue titled Update the HA-MCP Custom Component via HACS appears under Settings → Repairs. The server keeps running; update the component via HACS to clear it — see Held server updates if HACS does not show the update yet.

If the component was installed from the legacy location — the main ha-mcp server repository added directly as a HACS custom repository, before the dedicated ha-mcp-integration mirror existed — a repair issue titled Component installed from the legacy repository appears. Such an install keeps working, but HACS displays the server's 7.x version numbers and the server's release notes for the component. Follow the issue's link to add the mirror in HACS and reinstall the component from it (your settings and config entries are kept), then restart Home Assistant; the issue clears itself afterwards.

Held server updates

When a new server release also ships a newer custom component than the one you are running, the automatic server update is held instead of installed, and a repair issue titled HA-MCP server update waiting for a component update appears under Settings → Repairs to explain it. The hold exists to avoid auto-starting a server build under a component version it was never tested with — installing a newer server against an out-of-date component is the combination that has broken server startups. The server keeps running on its current version while the hold is in effect.

HACS usually shows no component update yet at this point. The component notices the newer server quickly — it checks PyPI every 6 hours and again on every reload or restart — but HACS refreshes its own repository information far less often: for a custom repository (how this component is installed in HACS) that can take up to about two days. So when the hold appears the component update is typically not yet visible in HACS, and telling Home Assistant to check for updates does not make HACS re-fetch any sooner. The one manual refresh that does is in HACS itself: open the HA-MCP Custom Component repository, open its (three-dot) menu, and choose Update information; the component update then appears.

The component asks HACS to perform this refresh automatically whenever the hold — or the component-outdated repair — first appears, so in most cases the component update shows up in HACS right away with no manual step. The manual Update information refresh above is the fallback for when that automatic request cannot reach HACS (for example HACS is still starting up, or its internals changed in a newer HACS release).

To clear the hold, update the component in HACS and restart Home Assistant. The held server update then installs automatically on the next periodic check or reload.

To install the update anyway, press Install on the HA-MCP server update entity — this bypasses the hold and installs the newer server immediately. Be aware that it runs a server build against a component it has never been tested with; if that server in turn requires an even newer component, it raises a separate Update the HA-MCP Custom Component via HACS repair until you update the component.

Local-only mode

Turn Remote access via webhook off to keep the server unreachable through Home Assistant entirely - no webhook means no Nabu Casa path and no /api/webhook/... endpoint. You keep direct access on the server port (with the secret path) and the admin-only sidebar panel.

Rotating your connect URL

If a connect URL may have leaked, open the entry's options and check Regenerate connect secrets now, then save - both the webhook secret and the direct-access path are re-minted on the spot and every old URL stops working. Update your MCP clients with the new URL from the Configure screen. (Removing and re-adding the entry also rotates everything, including the internal token.)

Security

The in-process server offers three authentication postures, chosen with the Authentication mode option:

  • none (default): the secret webhook URL is the credential. The webhook id is a high-entropy random string, and anyone who has the full URL can reach the server — exactly like the Webhook Proxy add-on's default. When exposed through Nabu Casa (or another HTTPS reverse proxy) the URL travels over TLS. Treat the URL like a password: don't share it or paste it where it could be logged.
  • ha_auth: clients sign in with your Home Assistant account. Home Assistant Core acts as the OAuth authorization server. MCP clients that support OAuth (for example claude.ai and ChatGPT) discover the sign-in endpoints automatically and authenticate the user against Home Assistant; requests without a valid Home Assistant token are rejected. Only administrator accounts are accepted: the server performs its Home Assistant operations with its own provisioned admin token, so a non-admin login is refused rather than silently granted admin-equivalent control. There is no separate password or credential to manage — it is your existing Home Assistant admin login.
  • legacy: a self-hosted OAuth server with a static Client ID + Secret. For OAuth-only clients that ha_auth can't serve (Google Gemini Spark, GitHub Copilot CLI). The component runs its own OAuth 2.1 authorization server at the Home Assistant root and issues a static Client ID / Client Secret to paste into the client — the secret is the credential and grants admin-equivalent access, so guard it like the none URL. Tokens are self-issued (1h access / 30d refresh) and carry no Home Assistant login; rotating the credential (regenerate toggle, or a custom Client ID / Secret override) invalidates outstanding tokens, but only after the Home Assistant restart the repair prompts for. See SECURITY.md for the full threat model (unauthenticated consent page, permissive redirect URIs, route ownership vs the add-on).

All three postures ride Home Assistant's own remote access (Nabu Casa / your reverse proxy) for TLS. If you expose the server to the internet, prefer ha_auth; keep the none URL — or a legacy Client Secret — strictly private.

The server reaches Home Assistant with a dedicated admin token the component provisions and stores in the config entry; that token is handed to the server in-memory (never through the Home Assistant process environment). Removing the entry revokes it. As with every deployment, that token's Home Assistant permissions define what the server can do.

See SECURITY.md for the full threat model.

Relationship to the File & YAML services entry

The in-process server entry and the HA-MCP File & YAML Tools entry are two config-entry types of the same HA-MCP Custom Component (ha_mcp_tools). They are independent: the server works on its own, and most installs never need the File & YAML entry. Add it only if you enable ha-mcp's opt-in file and YAML editing tools (feature flags, off by default) — those tools call the privileged services that entry registers, and that applies to every server type, including an external add-on, Docker, or stdio server as well as the in-process server. Add or remove it at any time from the same Add Integration menu (choose HA-MCP File & YAML Tools); it changes nothing about how the in-process server runs.

First start takes a little longer

The first time the server starts, the component downloads and installs the ha-mcp package with pip. This can take a minute or two — occasionally longer — depending on your connection and hardware; the server starts automatically once the install finishes. Later restarts are fast because the package is already installed.

Troubleshooting

The server won't start. If the server fails to come up — for example because the port is already in use, or token provisioning fails — a repair issue titled The HA-MCP in-process server failed to start appears under Settings → Repairs, carrying the specific reason. If the ha-mcp package itself can't be installed, the repair issue is titled The HA-MCP in-process server package could not be installed instead. Fix the cause — check the Home Assistant log and your network connectivity for an install failure, or set a different Server port for a port conflict — then reload the entry (save the options, or use ⋮ → Reload) to retry.

Nothing happens after updating the component. Home Assistant loads custom integration code at startup, so after HACS (or a manual copy) delivers a new version you must restart Home Assistant for the update to take effect.

Skill guidance is empty after installing from a GitHub tarball. The ha-mcp package (advanced) field can install from a GitHub tarball URL, but a git archive excludes submodules — and the bundled skill content ships as a submodule. A tarball install therefore omits it, so the skill-guidance tools report empty listings. Install from PyPI instead (either release channel includes the skill content); the tarball override is only meant for quick pre-release testing.

Where the logs are. The in-process server logs into the normal Home Assistant log (Settings → System → Logs, or home-assistant.log). Its working data lives in .ha_mcp/ under your Home Assistant config directory.

The Configure screen shows only a webhook path. If Home Assistant cannot determine an external or internal URL, the Configure screen and log show the webhook path on its own (/api/webhook/<webhook-id>); prefix it with your Home Assistant URL. Set your internal/external URLs under Settings → System → Network so the full URL is shown.