Esphome Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Esphome Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
An MCP server for the ESPHome "Device Builder" dashboard (2026.6 and newer). It lets an MCP client (Claude, etc.) list devices, read/edit/validate device YAML, stream logs, run diagnostics, and compile/flash firmware — speaking the dashboard's WebSocket command protocol.
Since ESPHome 2026.10 the dashboard has an MCP server of its own, so this one is no longer the only route. It is now best understood as the diagnostic companion to that built-in server: six of its tools have no built-in equivalent, and it stays useful for clients that cannot carry credentials. Read the next section before you wire either one up.
ESPHome 2026.10.0 bundles Device Builder 1.25.0, which serves its own MCP endpoint at `POST /api/mcp` — always on, no flag, 21 tools. Ten of this server's sixteen tools overlap it, and on the overlap the two are equivalent: measured live against a 2026.10.0 dashboard, get_config and get_device_configuration returned byte-for-byte identical YAML, the device facts matched field for field, and validation gave the same verdict.
So the choice is not about correctness. It is about these differences:
| this server | the dashboard's built-in /api/mcp | |
|---|---|---|
| Tools | 16 | 21 |
| Full device list (38 devices) | 9.4 kB — a summary written to be read | 42.7 kB — raw JSON, 43 fields per device |
| Device names only | 0.6 kB (list_device_names) | no equivalent — the full list is the only option |
| Tool schemas per session | 12.9 kB | 16.9 kB |
| Overwrite protection on save | none — edit_device_configuration writes regardless | yes — update_config refuses unless you pass the text you read |
| Credentials | logs in itself from .env | the client must send HTTP Basic; a bare request gets 401 |
| Where it runs | its own container, own URL, own healthcheck | inside the dashboard |
Use the built-in one for everyday device work — listing, reading, editing, validating, compiling, installing, plus everything in the "does better" list below. Use this one for diagnostics and bulk inspection: logs, connectivity probes, crash decoding, YAML migration, searching every config at once, and per-version schemas.
None of these exist as built-in MCP tools, on 1.25.0 or on 1.26.0. All six are thin wrappers over Device Builder commands the dashboard already implements — they are simply not wired to its MCP surface:
| Tool | Dashboard command it wraps |
|---|---|
get_device_logs | devices/logs |
troubleshoot_device | devices/troubleshoot |
decode_device_backtrace | devices/decode_backtrace |
migrate_device_configuration | editor/migrate_config |
search_device_configurations | yaml/search |
get_esphome_schema | the esphome-schema release archive — the built-in get_component takes no ESPHome version, so it can only describe the version the dashboard itself runs |
The intended endgame is unchanged: offer these upstream and retire this repo, rather than maintain a fork of tools the dashboard now owns.
expected copy of the text you read andanswers precondition_failed (with a diff) if the file moved under you. This server's edit_device_configuration has no such guard and will overwrite.
search_components, get_component,get_config_components, search_boards.
list_automations, get_available_automations,get_automation_docs, upsert_automation, delete_automation, with a real YAML splice.
list_secret_names.compile, get_job, cancel_job.If your dashboard has a password set, the built-in endpoint returns 401 until the client sends HTTP Basic auth — so a client that cannot attach headers cannot use it, which is one reason this server (which logs in from .env) is still the easier thing to point such a client at. The flip side: this server's own :8080 has no authentication of its own, so anything that can reach it can drive your dashboard, including firmware installs. Keep it off untrusted networks.
Why this fork exists. ESPHome 2026.6 replaced the dashboard's legacy HTTP API with a single WebSocket command protocol. The existing MCP servers (kdkavanagh/esphome-mcp,b2un0/esphome-mcp,jrigling/esphome-mcp-integration) all speak the old protocol, so config read/edit/validate return garbage against a 2026.6 server. This project keeps the clean tool layer fromkdkavanagh/esphome-mcpand rewrites the transport for the new protocol. SeeDECISIONS.mdfor the details.
Dashboard versions. The Device Builder ships fromesphome/device-builderon its own release cadence, so itsserver_versionis independent of the ESPHome version — 2026.10.0 ships Device Builder 1.25.0, 2026.9.x ships 1.14.9, 2026.8.0 ships 1.12.x, 2026.7.3 ships 1.7.0, and ESPHome'sdevbranch pins 1.26.0. (The pin is in ESPHome's owndocker/Dockerfile, which is the only place it is written down — and the only reliable way to tell whether a given ESPHome release has the built-in MCP server, since ESPHome's own changelog does not mention it.) This server is written against the 1.14.x protocol, verified against 1.25.0, and falls back to the pre-1.5.0 device shape where they differ. Its protocol reference is that repo'sdocs/API.mdandmodels/devices.py.
Upgrading from 2026.06.0? On ESPHome 2026.7 or newer it reported every device as unknown with no deployed version, and could report a successful install for firmware it never flashed. Both are fixed in 2026.08.0 — see the changelog.● the dashboard's built-in MCP server also does this (prefer it there) · ◆ only this server does this
| Tool | What it does | |
|---|---|---|
| ● | list_devices / list_device_names | Inventory configured devices |
| ● | check_device_update | Is a firmware update available? |
| ● | get_device_status | Online/offline + address |
| ● | get_device_version | Deployed vs current version |
| ● | get_device_configuration | Read a device's YAML |
| ● | edit_device_configuration | Save YAML (then auto-validate) |
| ● | validate_device_configuration | Full ESPHome validation, no save |
| ◆ | migrate_device_configuration | Respell legacy YAML keys for the installed ESPHome (dry run by default) |
| ◆ | search_device_configurations | Search every device's YAML for a string |
| ◆ | get_device_logs | Stream recent device logs |
| ◆ | troubleshoot_device | Live connectivity probe (DNS, mDNS, ping) |
| ◆ | decode_device_backtrace | Decode a crash backtrace into source locations |
| ◆ | get_esphome_schema | Component schema for a version |
| ● | install_device_configuration | Compile + OTA flash (destructive) |
| ● | update_device | Recompile + OTA flash to latest (destructive) |
The four ● device-fact tools (check_device_update, get_device_status, get_device_version, and the inventory) are all one built-in list_devices call upstream — it returns those fields inline. update_device has no distinct built-in verb; re-running the built-in install does the same thing.
Offline devices. If a device is offline, the dashboard compiles the firmware and arms it to flash on the device's next check-in.install_device_configurationandupdate_devicereport that asCOMPILED, FLASH DEFERRED— not success.
Config is via environment variables (12-factor). Copy .env.example to .env:
| Variable | Required | Description |
|---|---|---|
ESPHOME_DASHBOARD_URL | yes | Dashboard base URL, e.g. https://esphome.example.com or http://host:6052. REST and WebSocket URLs are derived from it. |
ESPHOME_DASHBOARD_USERNAME | no | Dashboard user. Required if the dashboard reports requires_auth=true — without it every command fails with not_authenticated. |
ESPHOME_DASHBOARD_PASSWORD | no | Dashboard password. |
LOG_LEVEL | no | DEBUG/INFO/WARNING/ERROR (default INFO). |
cp .env.example .env # then edit ESPHOME_DASHBOARD_URL
docker compose up -d --build
docker compose ps # STATUS should become "healthy"The server listens on :8080 and serves MCP over Streamable HTTP at http://<host>:8080/mcp. The container HEALTHCHECK performs a full MCP handshake and calls list_device_names, so it only reports healthy when the dashboard is actually reachable.
Once the registry image is published, pin it in compose.yaml:
image: ghcr.io/loryanstrant/esphome-mcp:latestPoint your client at the Streamable HTTP endpoint:
{
"mcpServers": {
"esphome": { "type": "http", "url": "http://<host>:8080/mcp" }
}
}For a stdio client, run esphome-mcp (instead of the web entrypoint) with the same env.
make install-dev # venv + deps
make check # lint + format-check + typecheck + test
# live tests against a real 2026.6 dashboard:
ESPHOME_DASHBOARD_URL=https://esphome.example.com .venv/bin/pytest -m liveThis project stands on the work of others (all MIT-licensed):
ESPHome MCP server. This fork keeps its FastMCP tool layer, schema handling, packaging and CI almost verbatim; the transport rewrite is the main change here.
image and surfacing the healthcheck / config-tool breakage that motivated this work.
— a Home Assistant integration referenced while mapping the ESPHome dashboard protocol.
The new 2026.6 WebSocket protocol was reverse-engineered from the ESPHome Device Builder front-end and verified against a live 2026.6 dashboard.
MIT.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.