Skip to main content
Glama
mattmaas

home-assistant-mcp

by mattmaas
README.md
# home-assistant-mcp

A [Model Context Protocol](https://modelcontextprotocol.io) server for
[Home Assistant](https://www.home-assistant.io/) over its REST API, in ~250 lines of
TypeScript with one dependency.

Two layers:

1. **Generic tools** — read states, call any service. Enough for an agent to operate a home
   it has never seen.
2. **A scene catalog** (optional) — a JSON file of *named presets with plain-English
   descriptions* that the agent can list and apply by name. "Make it cinema" becomes
   `ha_scene {"name":"cinema"}` instead of the model guessing 18 bulb entity ids and color
   temperatures. Presets are just `script.*` or `scene.*` entities you already have.

## Tools

| Tool | Purpose |
|---|---|
| `ha_ping` | Confirm the API is reachable and the token is accepted. |
| `ha_list_states` | List entity states; filter by domain / entity_id prefix / limit. |
| `ha_get_state` | Full state + attributes for one entity. |
| `ha_call_service` | Call any service (`light.turn_on`, `media_player.volume_set`, …) with optional target and data. |
| `ha_list_services` | All services grouped by domain. |
| `ha_list_scenes` | *(only when `HA_SCENES_FILE` is set)* List catalog presets. |
| `ha_scene` | *(only when `HA_SCENES_FILE` is set)* Apply a preset by name — enum-constrained so the model can't invent one. |

## Setup

```bash
npm install
npm run build          # tsc -> dist/server.js
HA_URL=http://homeassistant.local:8123 HA_TOKEN=... node dist/server.js
```

| Variable | Required | Default | Purpose |
|---|---|---|---|
| `HA_URL` | no | `http://homeassistant.local:8123` | Home Assistant base URL |
| `HA_TOKEN` | **yes** | — | Long-lived access token (Profile → Security) |
| `HA_SCENES_FILE` | no | — | Path to a scene catalog JSON (see `scenes.example.json`) |

MCP client registration (stdio):

```json
{
  "mcpServers": {
    "home-assistant": {
      "command": "node",
      "args": ["/path/to/home-assistant-mcp/dist/server.js"],
      "env": {
        "HA_URL": "http://homeassistant.local:8123",
        "HA_TOKEN": "<token>",
        "HA_SCENES_FILE": "/path/to/scenes.json"
      }
    }
  }
}
```

## Scene catalog pattern

The example catalog is the 13-theme setup this was written for. Each preset is layered in
Home Assistant so the same name works from any surface:

```
scene.home_<name>    canonical light state (scenes.yaml)
script.home_<name>   thin wrapper -> scene.turn_on, plus any extras (scripts.yaml)
ha_scene <name>      MCP -> script.turn_on via REST
```

Anything else (n8n, a dashboard button, a voice assistant) can call the same script, so the
agent and the humans share one vocabulary. Keep the descriptions honest — they are what the
model reads to choose.

## Notes

- `HA_TOKEN` is a secret; pass it via environment, never commit it.
- Use on a trusted network or via a VPN; this server does not add authentication of its own.

## License

MIT — see [LICENSE](LICENSE).