---
okf: "0.1"
type: runbook
visibility: private
status: active
updated: 2026-07-30
links:
  - ../services/ha-mcp.md
---

# ha-mcp — instalacja i rejestracja

## Install

The `mcp` SDK (and its pydantic/anyio/httpx dependency tree) is not packaged
for Debian and is not needed by anything else in this repo, so it goes into a
venv rather than into the system Python:

```bash
python3 -m venv --system-site-packages services/ha-mcp/.venv
services/ha-mcp/.venv/bin/pip install mcp pytest
```

`--system-site-packages` is deliberate: `requests`, `PyYAML` and
`websocket-client` are already installed system-wide and used by
`scripts/ha/lib/*`; the venv reuses those exact versions instead of shadowing
them with a second copy. `.venv/` is already covered by the repo `.gitignore`.

`pip install --break-system-packages mcp` also works and is one line shorter,
but it writes into the system interpreter that runs every deploy script on
this workstation — a venv keeps a 40-package dependency tree out of that blast
radius for a tool only Claude Code uses. Use the venv.

`run.sh` prefers `services/ha-mcp/.venv/bin/python` and falls back to the
system `python3`; if neither can import `mcp` it exits with one actionable
line on stderr rather than a traceback.

## Registration in Claude Code

`.mcp.json` in the repo root (project scope — shared with anyone who checks
out this repo):

```json
{
  "mcpServers": {
    "ha": {
      "command": "./services/ha-mcp/run.sh",
      "args": [],
      "env": {}
    }
  }
}
```

The command path is relative, so it resolves in any checkout (main or task
worktree) as long as Claude Code is started from the repo root. Start CC
there; on first run it asks whether to trust the project's MCP servers. Check
with `/mcp` — the server appears as `ha`, its tools as `mcp__ha__<tool>`.
Which repo the server reads is derived from its own location; override with
the `HA_MCP_REPO` environment variable if you ever need to point one checkout
at another's config.

## Standalone

```bash
./services/ha-mcp/run.sh                      # stdio server — speaks JSON-RPC on stdout
services/ha-mcp/tests/run.sh                  # offline test suite (no HA, no token)
services/ha-mcp/.venv/bin/python services/ha-mcp/tests/smoke_live.py [instance] [entity_id]
```

`smoke_live.py` is the only thing here that touches the network (GET only). A
one-off tool call without a client is easiest through the package:

```bash
PYTHONPATH=services/ha-mcp/src services/ha-mcp/.venv/bin/python -c "
from ha_mcp import tools; from ha_mcp.backend import LiveBackend
from ha_mcp.config import get_instance, repo_root
n, c = get_instance('ken', repo_root()); print(tools.instance_status(LiveBackend(n, c, repo_root())))"
```

## Tests

```bash
services/ha-mcp/tests/run.sh          # 42 tests, offline
```

Offline in the same sense as `scripts/ha/tests/*`: no network, no HA
instance, no token. Small hand-made fixtures under `tests/fixtures/` (shaped
like real `/api/states` and WebSocket registry payloads) cover the tool logic;
the repo's own `config/ken/automations/` and newest `fixtures/ken-states-*.yaml`
cover the repo-backed and at-scale paths (ranking over ~1650 real entities
behaves differently from ranking over ten).

Live smoke (read-only, run against `ken` on 2026-07-30):
`instance_status` → HA 2026.7.2, 1647 entities, 377 unavailable, 115
automations, 13 areas; `get_state("sensor.thsalon_temperature")` → `24.5 °C`.

