ガンバラナイ

HA用MCPは2系統ある。実機で比較してha-mcpに寄せた理由

HA用MCPは2系統ある。実機で比較してha-mcpに寄せた理由

はじめに

先日、Claude CodeからHome Assistantを自然言語で操作する記事を書きました。vibecode-agentアドオン+ローカルMCPサーバー+Cloudflare Tunnelという構成です。

しばらく使っていると、「これはこのMCPではできないのでは」「そもそもできるのか分からない」という場面がぽつぽつ出てきました。そこで、まったく別方式のMCPであるha-mcpも入れて、両方を並べて使いながら比べていました。その比較の途中で、前者の開発が止まっていることに気づきます。

結果として、当初「片方にしかない」と思っていた機能はほぼ全部ha-mcp側にあることが分かり、最終的にha-mcpへ一本化しました。

Home Assistant(以下HA)用のMCPは、大きく2系統あります。

  • アドオンでHTTP APIを公開し、ローカルのMCPサーバーがそこへ繋ぐ方式 — 前回組んだvibecode-agent構成
  • HAのプロセス内でMCPサーバーが動く方式 — ha-mcp

本記事では、まずha-mcpの特徴、次に前構成との比較、最後に実際に検証した結果と経緯を書きます。判断を誤って訂正した過程も省かずに書くので、そこが読みどころだと思います。

なお、ha-mcpのインストール・セットアップ手順は別記事にします。ここでは触れません。

検証時点の環境は次のとおりです。

  • HA Core 2026.7.2 / Home Assistant OS 18.1 / Supervisor 2026.07.3
  • ha-mcp サーバー 7.14.2 / コンポーネント 1.2.3(いずれも当時最新)
  • HACS 2.0.5 / vibecode-agentアドオン 2.10.47
  • エンティティ約430、ダッシュボード4、recorder DB 約241MiB

検証日は2026-07-23、YAML編集まわりの追加検証が2026-07-26です。

ha-mcpの特徴

HAの中で動く

ha-mcpは、HACS経由で配布されるHAのカスタムコンポーネント(ha_mcp_tools)とMCPサーバーの組み合わせです。最大の特徴はin-process運用ができること。MCPサーバーがHAプロセスの中で動くので、HAの外に「操作用のAPI」を別途生やす必要がありません。

HAのサイドバーに設定パネルが生えて、ツールの有効・無効、ベータ機能のトグル、サーバー再起動をGUIから操作できます。

前構成は「HAにアドオンを入れてHTTP APIを公開し、ローカルのMCPがそこへ繋ぐ」形でした。ha-mcpはHAの内側で完結します。この構造の違いが、そのままセキュリティ面の差になります。詳しくは比較の節で書きます。

ツールの被覆範囲

検証時点で確認できた守備範囲です。

領域 代表的なツール
検索・状態 ha_search(エンティティと自動化の中身を同時に横断検索)、ha_get_state、ha_get_overview
履歴・ログ ha_get_history(生の状態変化/長期統計の両対応)、ha_get_logs(logbook・システムログ・生ログ・アドオンログ・ログレベルをソース切替で1本化)
診断 ha_get_system_health(修復イシュー・設定検証・Zigbee/Z-Wave/Thread/Matterのネットワーク状況・統合ごとのdiagnosticsダンプまで含む)、ha_get_automation_traces
設定CRUD automation / script / scene / dashboard / helper(28種)/ラベル/カテゴリ/エリア・フロア/ゾーン
運用 バックアップ、アドオン、HACS、テーマ、エネルギーダッシュボード設定、Blueprint、更新管理
ファイル・YAML ha_read_file / ha_write_file / ha_list_files / ha_config_set_yaml(ベータ・既定OFF)

細かいところで良かったのは次の3点です。

  • ha_search は「エンティティ名」と「automation/scriptの設定本文」を並行して検索します。「このセンサーを使っている自動化はどれ?」が1コールで出ます。
  • ha_get_logs はlogbookが既定ソース。ログ系が1ツールに統合されています。
  • 修復イシューは読むだけでなくdismissもできます(ha_call_service(ws_command="repairs/ignore_issue") というWebSocketのエスケープハッチ経由)。

安全機構こそが差別化点

ha-mcpで面白いのは「できることの多さ」より「危ないことをできなくしてある」ほうです。4つ挙げます。

1. ベストプラクティス強制モード

書き込み系ツール(ha_config_set_automation など)を呼ぶと、初回は次のエラーで弾かれます。

BPS_ACKNOWLEDGMENT_REQUIRED — Strict best-practices mode is enabled for this tool.

サーバーが配布するスキルガイドを読み、その先頭に書かれたAcknowledgment key(read-receipt)を引数として渡し直すと、初めて書き込めます。しかもこのキーは毎時ローテートします。

つまり「LLMがHAのお作法を読んだこと」をプロトコルとして強制しているわけです。ガイドの中身も具体的で、「テンプレートよりnativeなtrigger/conditionを使え」「device_id ではなく entity_id を使え」「モーションライトのautomation modeは single ではなく restart」といった実践的なものが並びます。AIに設定を書かせるときの品質担保のアプローチとして、素直に感心しました。

2. 編集単位のバックアップ

automation / script / scene / dashboard / helper の編集をentity単位で保存します(検証環境では100世代保持)。list → diff → restore の流れで、HA再起動なしに直前の編集だけを戻せます。実際に試した結果は後述します。

3. ファイル・YAML編集は既定OFFのベータ扱い

有効化して初めてツールが生えます(マスタートグル+サブトグルの二段構え)。加えて、

  • YAML編集はキーのallowlist制(後述)
  • パストラバーサル遮断
  • confirm_token を渡さない1回目の呼び出しは何も書かず、unified diffだけを返す(2段階confirm)

この2段階confirmが、後の検証で非常に効いてきます。

4. .storage と secrets.yaml には恒久的に触れない

設定で開くこともできない、意図的な制限です。HAの内部状態DBをLLMに直接編集させない、という設計思想がはっきり出ています。

YAML編集のallowlist — 「専用ツールがあるものはYAMLで書かせない」

ha_config_set_yaml は、触れるトップレベルキーをallowlistで制限しています。この線引きが、そのままha-mcpの設計思想を表していて面白いので、節を分けて書きます。

許可キーは次のとおりです(存在しないキーを渡したら、エラーが全リストを返してきました)。

automation, binary_sensor, climate, command_line, cover, fan, group,
knx, light, mqtt, notify, recorder, rest, scene, script, sensor,
shell_command, switch, template, utility_meter

弾かれるキーには規則があります。次のヘルパー群がまるごと入っていません。

input_button, input_boolean, input_number, input_select, input_text,
input_datetime, counter, timer, schedule, zone, person, tag

これらは全部storage-collectionヘルパーという同じカテゴリで、HAが専用の作成API(WebSocketの <domain>/create)で扱う種類です。ha-mcpでは ha_config_set_helper という専用ツールが対応しています。つまり境界は「専用の作成APIを持つか」に、おおむね沿っています。

なぜYAML経路を塞ぐのか。確認できた事実から読み取れる理由は3つあります。

  1. 二重定義の衝突回避。これらはYAMLでもstorageでも定義でき、両方に同名があればentity_idが衝突します。実際 automation/script/scene は「packages配下でのみ許可、configuration.yaml では拒否」という扱いで、ツール自身が「storage-modeとYAML-modeを衝突させないため」と理由を明記してきます。
  2. 専用ツールのほうが優れている。ha_config_set_helper で作ればUIから編集でき、reload/restartなしで即反映されます。YAMLで書くと両方失います。
  3. ベストプラクティスの一貫性。ha-mcpのスキルガイドは「helperは専用APIかUIで作れ、YAMLは最後の手段」と明示しています。allowlistはこの方針をツールレベルで強制したものです。

ただし、この線引きは「専用ツールがあるかどうか」だけでは説明しきれません。template・utility_meter・group は専用ヘルパーがあるのに、許可されたままだからです。この3つをYAMLで書こうとすると、エラーではなく警告が返ります。「storage-modeの ha_config_set_helper があるからそちらを使え。ただしgit管理のpackagesならYAMLでよい」という内容です。

つまり本当の基準は「UIの代替があるか」ではなく「YAMLで管理することが正当か」。git管理のYAML設定は正当な運用なので通し、代わりに警告で誘導する。禁止と推奨を区別しています。

vibecode-agent構成との比較

比較対象は前回組んだ構成、すなわち home-assistant-vibecode-agent(HA側アドオン)と、それに繋ぐローカルMCPサーバー @coolver/home-assistant-mcp です。以下「vibecode-agent構成」と呼びます。

アーキテクチャの違い

【vibecode-agent構成】
Claude Code ──stdio──▶ ローカルMCP (npx) ──HTTPS──▶ [公開エンドポイント] ──▶ HAアドオン ──▶ HA
                                                          ↑
                                        HAを全操作できるAPIを外部公開する必要がある

【ha-mcp】
Claude Code ──HTTP──▶ HA本体 ─(in-process)─▶ MCPサーバー ──▶ HA
                        ↑
                HA本体のエンドポイントだけで済む

vibecode-agent構成では、HAを全操作できる専用API(Bearerキー1本で認証)を外に公開することになります。ha-mcpではその面が消えます。ここが比較の核心のひとつです。

前回の記事ではホスト名を ha-agent.example.com と伏せて書きましたが、いま思えば「実名を書けないエンドポイント」を1つ増やしていたわけです。それが消えるのは素直に嬉しい。

保守状況

方針転換の直接のきっかけがこれです。

ha-mcp vibecode-agent構成
開発状況 活発(検証時点でサーバー 7.14.2 が最新、コンポーネントも最新) 2026-05-14を最後に停止
既知の壊れ方 file/YAML周りに未修正の実行時バグは無し HA本体の更新で、オブジェクト/配列パラメータが落ちる不具合の実績あり(issue #36)

開発が止まったツールに日常運用を預けるリスク。これが「両方使う」をやめた最大の理由でした。

機能比較 — 「片方にしかない」はほぼ幻だった

当初は「vibecode-agent構成にしかできないこと」がいくつかあると思っていました。実際に確かめたら、ほぼ全部がha-mcp側にありました。

前構成にしかないと思っていた機能 ha-mcpでの代替 結果
設定ファイルの検証 ha_get_system_health(include="config_check") 代替あり
修復イシューの一覧 ha_get_system_health(include="repairs") 代替あり。dismissまで可能で上位互換
logbookの取得 ha_get_logs(source="logbook")(既定ソース) 代替あり
長期統計 ha_get_history(source="statistics") 代替あり
アドオンのログ ha_get_logs(source="supervisor", slug=…) 代替あり
エンティティレジストリ参照 ha_search / ha_get_entity 代替あり。前構成は aliases を返さないのでha-mcpが上位
アドオン/HACS/テーマ操作 ha_manage_addon / ha_manage_hacs / ha_manage_theme 上位互換
死んだエンティティの洗い出し ha_search(state_filter="unavailable") 代替あり

最終的にvibecode-agent構成にしか残らなかったのは、次の3つだけでした。

  1. .storage の直接編集 — ha-mcpは恒久的に禁止
  2. secrets.yaml への書き込み — ha-mcpは読み取り時にマスク、書き込み不可
  3. /config 全体のGitチェックポイント — ha-mcpに相当機能なし

しかも1と2はSSHアドオンやFile editorアドオンでもできます(=MCPの専有機能ではない)。3もha-mcpのスナップショットバックアップで代替できます(粒度は粗くなりますが)。

安全網の設計思想の違い

ha-mcp vibecode-agent構成
仕組み 編集単位バックアップ(entity単位・再起動不要・diff/restore) /config 全体のシャドウGit(自動コミット)
粒度 細かい 粗い(/config 丸ごと)
落とし穴 ファイル書き込みは対象外 自動コミットの契機がアドオン経由の書き込みだけ

最後の「落とし穴」については、私が盛大に勘違いしたので後述します。

実地で踏んだ挙動の違い

生々しいところも書いておきます。

  • 前構成でエンティティ名を更新すると、副作用で aliases に null が1件混ざることがあります(音声アシスタント用エイリアスの配列)。実害はAssist系のエイリアス解決に限られますが、まず気づきません。
  • timer ヘルパーは前構成の削除ツールの対象外で、以前は .storage を直接編集して消すしかありませんでした。ha-mcpでは通常のツールで消えます(検証済み)。
  • デバイスの「再有効化」だけは、どちらのMCPでもできません。 HA側の仕様で null を送る必要があるのですが、片方は無反応、もう片方は空文字がJSON化の過程で落ちます。最後の一手だけはHAのUIに残る、というオチでした。

実際に試した結果と経緯

ここからが本題です。判断を変えた過程をそのまま書きます。

出発点: 「競合ではなく相補」と整理していた

もともと2系統のMCPを両方登録し、「機能が重なる部分と、片方にしかない部分がある。使い分ければよい」と考えていました。手元のドキュメントにもそう書いていました。

転機: 開発が止まっていた

改めてリポジトリを確認したところ、vibecode-agent構成の最終更新が2026-05-14で止まっていました。加えて「HA本体の更新でパラメータが落ちる」という不具合報告(issue #36)も見つかりました。

保守が止まったコードに、HAを壊しうる操作と、その安全網(Gitバックアップ)の両方を預けている。 この構図に気づいたのが方針転換のきっかけです。

検証計画がレビューで「これでは何も分からない」と判明

ここは失敗談として価値が高いので詳しく書きます。最初に立てた検証計画には、次の欠陥がありました。

  • 削除の検証が「削除直後」しか見ていなかった。 本当の争点は「HAを再起動しても復活しないか」です。HAは内部状態を再構築するので、消えたように見えて復活することがあります。しかも検証対象に選んだヘルパーが、どちらのツールでも消せる種類だったのでそもそも差が出ません。→ 対象を「以前は .storage 直接編集が必要だった唯一の種類」に変更し、再起動後の再確認を工程に追加しました。
  • YAML破損バグの検証を、空の新規ファイルでやろうとしていた。 このバグは「既存の複数行テンプレートが巻き添えで壊れる」というものなので、空ファイルでは原理的に再現しません。
  • ロールバックの検証は、そもそも安全に実施する方法が無かった。 懸念は「内部状態が巻き戻ってHAと不整合になること」ですが、無害なテストでは再現しません。→ 検証をあきらめ、「この機能は使わない」と決めました(安全網として数えない、という判断)。
  • 書き込み系の不具合を、読み取り系ツールで確かめようとしていた。 症状はオブジェクト/配列パラメータの欠落なので、読み取りを叩いても検出できません。

教訓は「検証項目を並べる」より「その手順で本当に争点が判定できるか」を先に疑うこと。 特にAIに検証計画を立てさせると、もっともらしいが判定力のない項目が混ざります。

検証結果

作り直した計画で実施した結果です。

検証したこと 結果
編集単位バックアップのdiff → restore 合格。差分はRFC6902パッチ形式で変更箇所を正確に検出。復元後は設定のハッシュが完全一致、HA再起動も不要。しかも復元前に安全用スナップショットを自動生成していた
以前は .storage 編集が必要だったヘルパーの削除 合格。通常ツールで削除でき、再起動も不要
ダッシュボードの削除 合格
HA再起動後に、消したものが復活しないか(本命) 合格。復活せず、孤立したレジストリ残骸も無し
前構成の書き込み疎通 健全。報告されていた型の破損は現存せず(ただし開発停止中なので、次のHA更新で再発しても直る保証はない)

本命までの4項目が通った時点で、「.storage を直接編集する必要」が実質消えました。= vibecode-agent構成を残す理由が「最終手段」だけになった、ということです。

勘違いの訂正: 「Gitバックアップが壊れている」は誤りだった

検証の途中で、前構成の自動Gitコミットに未コミットが66件も溜まっているのを見つけました。当初は「機能が壊れている」と判断しました。

実際は違いました。自動コミットは正常に動いており、発火の契機が「アドオン経由の書き込み」だけだったのが真相です。新しいMCP経由で作業していたので、当然コミットされていなかった。前構成で書き込みを行った直後、自動コミットが4件追加されたのを確認して判明しました。

これは「新しい方に乗り換える判断」にとって不利な事実です。前構成の安全網は生きていた。それでも乗り換えを選んだのは、「開発停止したコードに預けるリスクのほうが大きい」と考えたからで、そこはトレードオフを承知で選んだということになります。

併せて、乗り換えで失うものも正直に書いておきます。ha-mcpに寄せる=ファイル書き込みがGit履歴に残らなくなります。補償として「破壊的作業の前にスナップショットを取る」運用に切り替えました。

ベータ機能の検証 — 「書かずに確かめる」

ファイル・YAML編集はベータ扱いで既定OFFです。有効化した上で、過去に報告されていたバグ(issue #1720。すでにcloseされている)が本当に解消しているかを確かめました。

このバグは、ファイル全体を再シリアライズするため、無関係な複数行テンプレートを壊すことがあるというもの。厄介なのは、壊れてもYAMLとしては妥当なままなので、HAは正常に起動しログも出ず、センサーだけが静かに壊れる点です。

検証設計の工夫として、「confirm_token を渡さない1回目は何も書かない」という仕様を利用し、実ファイルに対してpreviewだけを実行しました。これなら実害ゼロで本物の再現条件を試せます。

結果は条件付き合格。返ってきた差分がこれです。

@@ -1,4 +1,3 @@
-                          ← 先頭の空行が消える
 # Loads default set of integrations. Do not remove.
   trusted_proxies:
-    - 172.30.33.0/24      ← インデントが 4 → 2 に正規化される
-                          ← 行末空白だけの行が消える
+  - 172.30.33.0/24
+shell_command:
+  zz_test_noop: /bin/true ← ここだけが意図した追加

コメントとHA独自タグ(!include 系)は保持されました。一方で、触っていない行の再フォーマットが差分に混ざります。YAMLとしての意味は変わらない(インデント幅も空行も等価)ので実害はありませんが、「ファイル全体を再シリアライズしている」ことの裏付けにはなります。

正直に書いておくと、この環境の設定ファイルには複数行テンプレートが無かったので、バグ本体(テンプレート破損)は判定できていません。再現条件が無かったので未判定、というのが正確なところです。

そこで運用ルールとして、「必ずpreview → 差分レビュー → confirmの2段階を踏む」「複数行テンプレートを含むファイルはこのツールを使わず、ファイル読み書きで全文を扱う」と決めました。

なお、ha-mcp側にはツールセキュリティポリシーの承認ゲートが素通りすることがあるというOPENのissue(#1990)もあります。サーバー側の承認機構を安全網として当てにしないほうがよさそうです。

エラーメッセージが嘘をつく

検証中、特定のツールだけが次のエラーで落ちました。

CONNECTION_FAILED / "Event loop is closed"
CONNECTION_FAILED / "…got Future <…> attached to a different loop"

エラーは「HAが動いているか確認してください」「URL設定を確認してください」と示唆してきます。どちらも誤りでした。 同じセッションで他のツールは正常に応答していたからです。実際はMCPサーバー内部のWebSocketクライアントがイベントループを跨いで壊れていた状態で、その経路を使うツールだけが落ちていました。

対処は「別ツールで迂回する」「MCPを再接続する」「サーバーを再起動する」。エラーメッセージの示唆を鵜呑みにせず、他のツールが通るかを先に確かめる、という切り分けの実例になりました。

実運用でallowlistの壁にぶつかる

一本化した後、実際にpackageファイル(packages/*.yaml)を書く場面でallowlistの線引きに突き当たりました。作りたかったのは「templateセンサー群+statisticsセンサー+リセット用の input_button」を1ファイルにまとめた構成です。

ha_config_set_yaml で書こうとすると、template と sensor は通るのに input_button だけが "Key 'input_button' is not in the allowed list" で即エラー。このときエラーが全許可キーを列挙してきて、先述の規則(storage-collectionヘルパーは専用ツール送り)に気づいた次第です。なお読み取りの ha_config_get_yaml はallowlistと無関係で、任意キーを読めます。

ここでも検証は「書かずに」やりました。2段階confirmを使って template / input_button / command_line を実ファイルに対してpreview実行し、返ってくるdiffとエラーだけを見ています。

回避策は2つ。①ファイルを1ツールで完結させたいなら、input_button をYAMLから外して ha_config_set_helper でstorage-modeヘルパーとして作る(=ha-mcpが本来意図している作り方)。②どうしても1ファイルに全部入れたいなら、ha_config_set_yaml は使わず、ファイル書き込み系ツールで全文を扱う。

なお、あとで調べて分かったのですが、コンポーネント1.2.4以降には「Extra YAML write keys」という設定が追加されており、許可キーを運用者が足せるようになっています。検証環境はちょうど1つ手前の1.2.3で、この設定はまだありませんでした。ただしこれを使っても、homeassistant / http / frontend / lovelace の4キーだけは追加できません。HA自身の認証設定を書き換えたり(http: の trusted_proxies は認証バイパスに使えます)、認証済みダッシュボードに任意のJavaScriptを読み込ませたり(frontend: の extra_module_url)できてしまうキーで、運用者にも開放されない床として別枠で拒否されています。allowlistを緩められる余地を作りつつ、そこだけは動かせないようにしてある、という設計です。

つまりallowlistは不便な制限ではなく、ha_config_set_helper という正規ルートへの誘導でした。「1ファイルにまとめたい」という素朴な要望と、「helperはstorage-modeで持て」というHAの作法が衝突する具体例として、面白いところです。

一本化へ

再調査の結果、vibecode-agent構成に残る依存は3つだけ(.storage 編集/secrets.yaml 書き込み/Gitチェックポイント)で、1と2はMCPの外に、3もha-mcp内の別機能に代替経路があると分かりました。

乗り換えで得るもの。

  • HAを全操作できる専用APIを外部公開しなくてよくなる(攻撃面がひとつ減る)
  • 開発停止した実装への依存がゼロになる

失うもの。

  • /config 全体の任意時点コミット
  • 冗長性。実際、前述の障害時にはvibecode-agent構成で迂回できました。ただしその場面はha-mcp内の別ツールでも代替できたので、冗長性の実効価値は見た目ほど大きくない

引き返せるよう、段階的に進める方針にしました。

  1. しばらくvibecode-agent構成を使わない期間を置く
  2. MCPの登録を外す
  3. アドオンを停止する
  4. 公開ルートとDNSレコードを削除する

まとめ

  • HA用のMCPは「アドオンでHTTP APIを公開する方式」と「HAのプロセス内で動く方式(ha-mcp)」の2系統。構造の違いがそのままセキュリティ面の差になり、後者はHAを全操作できるAPIを外部公開しなくて済む。
  • ha-mcpの価値は機能の多さより安全機構にある。ベストプラクティス強制モード、entity単位のバックアップ、YAML編集の2段階confirmとallowlist、.storage・secrets.yaml の恒久禁止。「危ないことをできなくしてある」設計。
  • 「片方にしかない機能」は確かめるとほぼ幻で、残ったのは3つだけ。それも代替経路がある。思い込みで相補と整理していたのを実機検証で覆したのが今回の収穫。
  • 検証計画は「項目を並べる」より「その手順で争点が判定できるか」を疑うほうが大事。再起動後の確認が無い、空ファイルでは再現しないバグを空ファイルで試す、など判定力の無い項目が最初は混ざっていた。
  • 未判定の部分もある。 YAML再シリアライズによるテンプレート破損バグ本体は、再現条件が手元に無く確認できていない。preview → 差分レビュー → confirmを必ず踏む運用でカバーしている。

次はha-mcpのインストールとセットアップを別記事にまとめる予定です。