AI エージェント「Hermes Agent」を、Docker コンテナに閉じ込めて、Mac の Desktop アプリから使う構成を作りました(構成と、そのときのハマりどころはこちらの記事に書いています)。
普段の Claude Code では、BigQuery を読むための自作の MCP サーバーを使っています。これを Hermes からも使えるようにしてみました。
ところが、Claude Code と同じ感覚で登録しようとしたら、いくつも罠がありました。
この記事でわかること
- Hermes 本体が Docker の中にいると、stdio 方式の MCP が使えない理由
- Mac 側の MCP サーバーを HTTP にして、コンテナから呼ぶ手順
421 Misdirected RequestとCancelled.の罠
かかった時間:相談を始めてから、Hermes に登録できるまで40分ほどでした。
つなぐ MCP サーバー
つなぐのは、BigQuery を読むことしかできない自作の MCP サーバーです。ツールは6つで、SELECT 以外の SQL と、読む量が上限を超えるクエリは実行されません。作り方と守り方は「読み取り専用の BigQuery MCP を自作した記事」に書きました。
Hermes に MCP をつなぐ2つの方式
Hermes の MCP 設定は ~/.hermes/config.yaml の mcp_servers: に書きます。方式は2つあります。
| 方式 | 内容 | 今回 |
|---|---|---|
| stdio | Hermes がサーバーを子プロセスとして起動する | ✕ |
| HTTP | 起動済みのサーバーに URL で接続する | ◯ |
Claude Code では stdio 方式で登録しています。ところが、今回の Hermes は本体がコンテナの中にいます。stdio だと「Hermes が動いている場所」でサーバーを起動するので、コンテナの中から Mac のファイルや uv、Google Cloud の認証情報が見えず、動きません。
選択肢を比べた
| 案 | 内容 | 評価 |
|---|---|---|
| A. Mac で HTTP 待ち受け | Mac でサーバーを HTTP で起動し、コンテナから接続 | 採用 |
| B. コンテナにマウント | サーバーのコードと認証ファイルをコンテナにマウントして stdio で起動 | コンテナに uv はあったので可能。ただし認証情報がエージェントの隣に置かれる |
A 案を選んだ理由は3つです。
- Google Cloud の認証情報を Mac の外に出さずに済む
- コンテナのイメージや compose を変えずに済む
- サーバーは読み取り専用でも、クエリは自分の権限で走る。エージェントから認証情報は遠ざけておきたい
「見える範囲を絞る」ために Docker に入れたので、そこに認証ファイルを持ち込んでしまっては本末転倒です。
手順
1. Mac 側で MCP サーバーを HTTP で起動する
サーバーに --http --port オプションを足しました。待ち受けは 127.0.0.1 に固定し、FastMCP の DNS リバインディング保護も有効のままにしています。
def _run_http(port: int) -> None:
# ADC(本人の Google ID)でジョブが走るため、同一 Mac からの接続に限定する。
# 待ち受けはループバック固定。Host / Origin は localhost 系と、同じ Mac の Docker コンテナ
# (Docker Desktop の host.docker.internal)のみ許可する(DNS リバインディング対策)。
mcp.settings.host = "127.0.0.1"
mcp.settings.port = port
mcp.settings.transport_security = TransportSecuritySettings(
enable_dns_rebinding_protection=True,
allowed_hosts=[f"127.0.0.1:{port}", f"localhost:{port}", f"host.docker.internal:{port}"],
allowed_origins=[
f"http://127.0.0.1:{port}", f"http://localhost:{port}", f"http://host.docker.internal:{port}"
],
)
mcp.run(transport="streamable-http")
uv run servers/bigquery/server.py --http --port 8790
ポイント
host.docker.internalを許可リストに入れないと 421 エラーになります(後述)- 待ち受けは
127.0.0.1のままでも、Docker Desktop for Mac のコンテナからは届きました。サーバーのログを見ると、接続元は127.0.0.1になっていました。外のネットワークには公開されません
手で起動していると、サーバーが止まった瞬間に Hermes から使えなくなります。そこで、このコマンドを launchd で常駐させました。plist で RunAtLoad と KeepAlive を有効にして、ログイン時に起動し、落ちたら起動し直すようにしています。この記事の接続も、最初から launchd で動かしているサーバーに対して行いました。
同じタイミングで、以前から使っていた別の BigQuery MCP(MCP Toolbox を使ったもの)は機能が重複していたので廃止し、こちらに一本化しました。
2. コンテナの中で Hermes に登録する
docker exec -it hermes hermes mcp add bigquery --url http://host.docker.internal:8790/mcp
# Does this server require authentication? [Y/n]: n
# ✓ Connected! Found 6 tool(s) from 'bigquery':
# Enable all 6 tools? [Y/n/select]: y
# ✓ Saved 'bigquery' to ~/./config.yaml (6/6 tools enabled)
# Start a new session to use these tools.
(出力は抜粋です。実際には、この間に6つのツール名と説明が表示されます)
ポイント
- Mac の
hermesコマンドではなく、docker execでコンテナの中の CLI を呼ぶ -itを必ず付ける(後述)- 認証なしのサーバーなので
n
3. 接続を確認する
docker exec hermes hermes mcp test bigquery
# Transport: HTTP → http://host.docker.internal:8790/mcp
# Auth: none
# ✓ Connected (678ms)
# ✓ Tools discovered: 6
Desktop アプリで新しいセッションを開き、データセットの一覧を頼んでみました。

2つのデータセットが返ってきて、Claude Code から同じツールを呼んだ結果と一致しました。
ハマりポイント
罠①:Mac の hermes コマンドが動かない
Claude Code の MCP 設定を取り込むコマンド(hermes import-agent claude-code)を Mac のターミナルで打ったら、No such file or directory になりました。
原因は、~/.local/bin/hermes に以前のインストールで置かれたランチャーが残っていて、参照先がもう消えていたことでした。参照先は ~/.hermes の中のローカル版で、Docker で動かす構成を作ったときに、「Desktop アプリが勝手にローカル版をインストールした」件で消したものです(詳しくはこちらの記事)。Hermes 本体はコンテナの中にいるので、CLI は docker exec -it hermes hermes ... で呼ぶのが正解でした。
なお、import-agent をコンテナの中で動かしても、Mac 側の Claude Code の設定ファイルはコンテナから見えないので、この構成では使えないと判断しました(試してはいません)。
教訓:まず「Hermes がどこで動いているか」を確認する。stdio の MCP は、Hermes が動いている場所で起動される。
罠②:コンテナから接続すると 421 Misdirected Request
コンテナの中から http://host.docker.internal:8790/mcp に接続すると、421 が返ってきました。サーバーのログには Invalid Host header の警告と、"POST /mcp HTTP/1.1" 421 Misdirected Request が出ていました。
原因は、FastMCP の DNS リバインディング保護です。許可リストにない Host ヘッダー(host.docker.internal:8790)を拒否していました。許可リストに host.docker.internal を追加して、サーバーを再起動したら通りました。
教訓:421 は「届いたけど Host を拒否された」というサイン。ネットワークがつながっていないわけではない。
保護そのものを切るのではなく、許可するホストを1つ足すだけにしています。
罠③:hermes mcp add が最後に「Cancelled.」になる
接続に成功してツールが6件表示されたのに、最後に Cancelled. と出て設定が保存されませんでした。mcp test を打っても、設定にないと言われます。
✗ Server 'bigquery' not found in config.
原因は、docker exec に -it を付けていなかったことでした。対話式の質問に入力が渡らず、キャンセル扱いになっていました。このとき GetPassWarning: Can not control echo on the terminal. という警告も出ていました。
教訓:対話式の CLI をコンテナで動かすときは -it を付ける。
罠④:引数を付け忘れる
hermes mcp add だけを打つと、サーバー名が必要だと言われます。
hermes mcp add: error: the following arguments are required: name
hermes mcp add bigquery --url ... のように、名前と URL を指定します。
確かめた環境
- iMac(M3)、Docker Desktop for Mac
- Hermes Agent v0.21.5(Docker イメージ
nousresearch/hermes-agent:v2026.9.24)、Desktop アプリは同じタグから自分でビルドしたもの - MCP サーバーは Python の MCP SDK(1.x)の FastMCP で作ったもの。2.x では
mcp.server.fastmcpの import が通らなくなるので、こちらの記事のように 1.x に固定しています
使ってみて
Hermes から BigQuery を使ってみると、Claude Code から聞いて使うのとほぼ同じことができたかなと思います。
使い分けは、Claude Code は主に開発用、Hermes Agent は秘書のような使い方を考えています。運用している自作ツールの状況や、最近やったこと、ヘルスケアのデータなどを貯めているので、それを好きなときに取り出したり、それについて相談したり、次のネタを探したりするときに使おうと思っています。
Claude Code に直接聞いてもよいのですが、Claude Code はセッションごとに、そのリポジトリのフォルダまで移動して起動しています。ちょっと聞きたいだけのときに、毎回それをするのは面倒です。Hermes なら、BigQuery のデータは MCP 経由でそのまま取れます。こういう「自分の情報を聞く」使い方は、将来は Hermes に集めたいと思っています。
BigQuery 以外の場所に貯めている情報(AI とのセッションのログなど)もあるので、次はそれを取り出す MCP を作ってみようと思っています。作ったら、また書いてみようと思います。
まとめ
- Hermes 本体が Docker の中にいるなら、Mac 側の MCP は stdio ではなく HTTP で公開する
- コンテナからは
host.docker.internal:<ポート>で届く。待ち受けは127.0.0.1のままでよかった - FastMCP の Host 許可リストに
host.docker.internalを足さないと 421 - コンテナの中の CLI を対話で使うときは
docker exec -it
1つの MCP サーバーを Claude Code と Hermes の両方から使えるようになり、道具を増やしても、同じデータに同じ仕組みを通してアクセスできるようになりました。
あわせて読みたい
