Skip to main content
Glama
skoniog

hydra-ops-mcp

by skoniog

hydra-ops-mcp

Hydraヘッドと会話して操作する。 実行中のヘッド(ライフサイクル、台帳、L1ウォレット、ノードログ、オンチェーンエラーコード)を、LLMクライアントが呼び出せるツールとして公開するMCPサーバーです。TUI、curl、cardano-cli、docker logsを切り替える代わりに、平易な言葉でヘッドを操作・デバッグできます。

init、デポジット、ヘッド内トランザクション、decommit、close、fanout、部分ファンアウト、デポジットリカバリなど、運用に関わる全操作をカバーします。さらに、ヘッド状態とL1の読み取り専用ビューも提供します。状態を変更するすべての操作は、実行前にその内容を説明し、明示的な確認を待ちます。


目次


Related MCP server: mcp-cli-catalog

なぜ

ヘッドを運用するには、複数のツールを同時に扱う必要があります。TUIはヘッドの状態を表示しますが、トランザクションが拒否された理由はわかりません。WebSocket APIはイベントを提供しますが、JSONを手動で解析する必要があります。問題が発生した場合、答えは通常docker compose logsにあり、それをヘッドの状態と照合し、Plutusソースコードにあるエラーコードとデコードする必要があります。

このサーバーは、これらすべてを1つの対話型インターフェースの背後に配置します。

「ヘッドがファンアウトしません。何が問題ですか?」

Claudeはヘッドの状態を確認し、ノードログから失敗したトランザクションを取得し、H39アボートコードをFanoutUTxOHashMismatchにデコードし、実際にそれを引き起こす2つのことを伝えることができます。これらすべてを1ターンで行えます。なぜなら、ヘッドAPI、コンテナログ、エラーテーブルすべてにアクセスできるからです。

また、ルーチン的な部分(ヘッドの開設と資金投入、資金移動、決済)にも役立ちます。各ステップは実行前に説明され、確認されます。そして、単一のノードにバインドされたTUIセッションとは異なり、すべてのツールはnode引数を受け取るため、alice、bob、carolが同じヘッドについてそれぞれ何を信じているかを比較できます。

現在はhydraデモdevnet(3ノード、3パーティ)を対象としています。APIレイヤーはdevnet固有ではありませんが、L1ヘルパーとキー管理はdevnet固有です(制限事項を参照)。


クイックスタート

前提条件 — Docker、Python 3.10+、およびcardano-scaling/hydraのチェックアウト(デモdevnetとPlutusエラーテーブル用)。

git clone https://github.com/skoniog/hydra-ops-mcp && cd hydra-ops-mcp
python3 -m venv .venv                      # or: uv venv .venv
.venv/bin/pip install -r requirements.txt

./reset_devnet.sh                          # cardano-node + 3 hydra-nodes, seeded

サーバーをMCPクライアントに登録します。Claude Code:

claude mcp add hydra-ops -- /absolute/path/to/hydra-ops-mcp/.venv/bin/python \
    /absolute/path/to/hydra-ops-mcp/server.py

Claude Desktop — claude_desktop_config.jsonに追加:

{
  "mcpServers": {
    "hydra-ops": {
      "command": "/absolute/path/to/hydra-ops-mcp/.venv/bin/python",
      "args": ["/absolute/path/to/hydra-ops-mcp/server.py"]
    }
  }
}

MCPランチャーはサーバーを環境変数を削減した状態で起動するため、シェルでエクスポートするのではなく、オーバーライド(HYDRA_DEMO_DIR、HYDRA_REPO)は"env"ブロックで渡してください。

その後、次のように質問します:

「ヘッドの状態はどうなっていますか?aliceはL1で何を保持していますか?」 「ヘッドを開設し、aliceの資金をコミットしてください。」 「aliceからbobに5 ADAを送金し、ヘッドのUTXOセットを表示してください。」

この方法でヘッドを操作するのが初めてですか? RUNBOOK.md では、ライフサイクル全体(開設、資金投入、トランザクション、デコミット、クローズ、決済、意図的な破壊)を一連のガイド付きセッションとして説明しています。


アーキテクチャ

  MCP client (Claude Code / Claude Desktop / anything speaking MCP)
        │  stdio
        ▼
  server.py                 FastMCP registration; thin wrappers only
        │
  tools/                    one module per domain, plain functions
   ├── observe.py           head state, UTXOs, L1 funds, params, events
   ├── lifecycle.py         init, commit, decommit, close, fanout, recover
   ├── transact.py          in-head transfers
   ├── diagnose.py          node logs, error-code decoding
   └── types.py             ok() / err() / needs_confirmation()
        │
        ├──▶ hydra_client.py    WebSocket + HTTP to hydra-node
        │                       async core, sync facade, event buffer
        ├──▶ tx_builder.py      PyCardano: build + sign in-head txs
        ├──▶ cardano.py         cardano-cli in the node container (L1)
        └──▶ errors.py          parses hydra-plutus for abort codes

hydra_client.py はノードごとにWebSocket接続を保持し、同期ファサードの背後でデーモンスレッド上で非同期イベントループを実行します。これにより、ツール関数はプロトコルイベントを待機しながらもシンプルに保たれます。recent_eventsのためにすべてのサーバー出力をバッファリングし、ヘッドステータスを追跡し、確認済みトランザクションを関連付けます。コマンドは、楽観的に返すのではなく、特定の結果イベント(Decommit → DecommitFinalized、Fanout → HeadIsFinalized)を待機するため、成功したツール呼び出しはプロトコルステップが実際に完了したことを意味します。

tx_builder.py はPyCardanoを使用してトランザクションを構築および署名します。トランザクションごとにcardano-cliを往復する必要はありません。cardano.py はL1側(アドレス導出、UTXOクエリ、デポジットトランザクションの署名と送信)を処理し、実行中のcardano-nodeコンテナ内でcardano-cliをexecします。キーもそこにあります。

errors.py は、呼び出し時にローカルのhydraチェックアウトからHeadError.hs、DepositError.hs、HeadTokensError.hsなどを解析するため、デコードされたコードは常に実行中のバージョンと一致し、古くなるテーブルとは一致しません。


確認モデル

状態を変更するすべてのツールはconfirm: bool = Falseを受け取ります。これなしで呼び出された場合、ツールは可能な限りすべてを検証し、実際に何を行うかを解決し、何も変更せずに説明を返します:

{
  "status": "requires_confirmation",
  "action": "deposit alice's UTXO 4a3f…#0 (100,000,000,000 lovelace) into the head via node 1",
  "message": "This would deposit… Nothing has been done. Retry with confirm=True to execute.",
  "party": "alice", "utxo_ref": "4a3f…#0", "lovelace": 100000000000
}

実際には、Claudeが提案し、あなたが承認し、その後でのみオンチェーンで何かが行われます。これは、一方的で不可逆的な操作(close_headはヘッド内のすべての参加者に影響を与え、fanoutはヘッドの最終状態を確定します)で最も重要です。

プレビューは仮説ではなく解決済みです。commit_fundsは選択した正確なUTXOを指定し、decommitはヘッドのUTXOセットから導出した所有者と金額を指定し、send_txは構築したトランザクションIDを報告します。検証はゲートの前に実行されるため、失敗するはずの操作を確認するよう求められることはありません。読み取り専用ツールにはゲートがなく、即座に実行されます。


ツールリファレンス

すべてのツールは{status, error, ...}を返します。失敗は例外ではなく{"status": "error", "error": "<message>", ...}です。l1_fundsとexplain_errorを除き、すべてのツールはnode: int = 1(1 = alice、2 = bob、3 = carol)を受け入れます。

可観測性(読み取り専用)

ツール

シグネチャ

戻り値

head_status

(node=1)

ヘッドタグ、WSで観測されたステータス、UTXO数、総lovelace、スナップショット番号、ヘッドバージョン、異議申立期限

head_utxos

(node=1)

アドレスごとにグループ化されたヘッドのUTxOセット、各々に参照と値

l1_funds

(party="alice")

パーティのL1アドレス、UTXO数、総lovelace、UTXOごとの値

protocol_parameters

(node=1)

ヘッドの台帳パラメータの完全なセットと、問題を引き起こすもの(手数料、最小UTXO、サイズ)の概要

pending_deposits

(node=1)

観測されたがまだ吸収されていないデポジット — リカバリ候補

recent_events

(node=1, tag=None, limit=25)

この接続で見られたサーバー出力。オプションでタグでフィルタリング可能

recent_eventsはサーバー接続以降のイベントをカバーします。WS接続は履歴を要求しないため、完全なログではなくライブテールです。それより古いものについてはnode_logsを使用してください。

ライフサイクル(確認ゲート付き)

ツール

シグネチャ

備考

init_head

(node=1, confirm=False)

ヘッドがIdleでない限り拒否します。2.3.0ではヘッドは即座に空の状態で開きます。資金はデポジットで後から投入されます

commit_funds

(party="alice", node=1, utxo_ref="", confirm=False)

POST /commitでデポジットを作成し、パーティの資金キーで署名し、L1に送信し、吸収を待ちます。1つのUTXOをデポジットします。utxo_refで別のUTXOが指定されない限り、最大のものが選択されます

decommit

(utxo_ref, node=1, confirm=False)

ヘッドが開いたまま、1つのヘッドUTxOをL1に引き出します。UTxOのアドレスから所有者を導出し、全額の自己転送をデコミットトランザクションとして構築します

close_head

(node=1, confirm=False)

最新の確認済みスナップショットを投稿し、異議申立期間を開始します。すべての参加者に影響します

fanout

(node=1, confirm=False)

必要に応じてReadyToFanoutを待ち、UTxOセット全体をL1に分配します

partial_fanout

(utxo_refs, node=1, confirm=False)

選択したサブセットを決済します。分配されたものと残ったものを報告します。制限事項を参照 — 2.3.0より新しいノードが必要です

recover_deposit

(tx_id, node=1, confirm=False)

DELETE /commits/{txid} — スタックしたデポジットをL1に戻します

commit_fundsは呼び出しごとに1つのUTXOをデポジットします。複数UTXOのデポジットは、2.3.0でH39によるファンアウトを妨げる原因となります(運用上の注意を参照)。

トランザクション(確認ゲート付き)

ツール

シグネチャ

備考

send_tx

(sender, receiver, amount_lovelace, node=1, confirm=False)

ヘッド内転送。senderは署名キーが利用可能なパーティ名。receiverはパーティ名またはbech32アドレス

1 ADA未満の金額は拒否されます。ヘッドは最小UTXOをゼロにするため、そのような出力はL2では有効ですが、L1で再作成することは不可能になり、ファンアウトを恒久的に妨げます。トランザクションは確認時に現在のUTxOセットに対して再構築されるため、古いプレビューが古いインプットを使用することはありません。この呼び出しは、トランザクションが受け入れられた時点ではなく、確認済みスナップショットに表示された時点で返ります。

診断(読み取り専用)

ツール

シグネチャ

備考

node_logs

(node=1, pattern="", since="10m", limit=40)

コンテナログ。オプションで正規表現フィルタリング可能。一致した行数と最後のlimit行を返します

explain_error

(code)

アボートコード(H39、D01、…)を、ローカルのhydraチェックアウトからそのコンストラクタとモジュールにデコードします。実際に発生するものについては実用的な注意事項も含みます


hydra-tuiとの比較

ツールの表面は意図的にhydra-tuiが公開するものと一致しているため、TUIでできることはすべてここでも実行できます:

hydra-tui

こちら

i — init

init_head

commit dialog

commit_funds(ドラフト、署名、送信、吸収待ち)

n — new transaction

send_tx

d — decommit

decommit

c — close

close_head

f — fanout

fanout

p — partial fanout

partial_fanout

r — recover deposit

recover_deposit

main tab

head_status, head_utxos

funds tab

l1_funds

event history tab

recent_events

—

protocol_parameters, pending_deposits

TUIと同様に、Contest、SafeClose、SideLoadSnapshotは公開されていません。これらは特定のオンチェーン状態に対するプロトコルの応答であり、1つのアクションが正しく、タイミングが重要です。これらはプロンプトの背後ではなく、アラート付きの決定論的ツールに属します。

さらに進んだ点:

  • 診断。 node_logsとexplain_errorにはTUIに相当するものはありません。これが最大の実用的な利点です——スタックしたヘッドが「TUIは失敗したと言っている」状態から、デコードされたアボートコードと一致するログ行に変わります。

  • クロスノード。 TUIセッションは1つのノードにアタッチします。ここではすべてのツールがnodeを受け取るため、alice、bob、carolが同じヘッドについてそれぞれ何を信じているかを尋ねることができます——遅れているノードを特定する最速の方法です。

  • L1とL2を一緒に。 l1_fundsはチェーンを直接クエリするため、「そのデコミットは実際に着地したのか?」という質問が1つで済み、cardano-cliへのコンテキストスイッチは不要です。

  • ガードレール。 最小UTXO未満の出力とマルチUTXOのデポジットは、構築段階で拒否されます。なぜなら、どちらも後でファンアウトをサイレントにスタックさせるからです。

  • 構成。 複数ステップの操作が1つのリクエストで行われます:「ヘッドを閉じて、異議申立期間を待ち、ファンアウトして、全員の最終L1残高を表示して」 は単一の依頼です。

それでもTUIが勝る点: ライブダッシュボードです。MCPはリクエスト/レスポンスなので、継続的に更新されるビューではなくスナップショットが得られます——時間経過に伴うヘッドの監視にはTUIを開いたままにしてください。また、反復作業ではキーストロークがモデルのラウンドトリップよりも優れており、TUIのUTxOピッカーは視覚的ですが、ここではリストしてから選択します。


設定

すべてはconfig.pyにあり、環境変数で上書きできます:

設定

デフォルト

意味

NODES

4001, 4002, 4003 on localhost

ノードインデックス → WS/HTTPエンドポイントとパーティ名

HYDRA_DEMO_DIR

/home/dev/claudecode/hydra/demo

デモdevnet:docker composeプロジェクトと認証情報

HYDRA_REPO

/home/dev/claudecode/hydra

Hydraチェックアウト、アボートコードのデコード用

NETWORK_MAGIC

42

Devnetマジック

MIN_OUTPUT_LOVELACE

1_000_000

ヘッド内出力の拒否しきい値

署名鍵はデモの{alice,bob,carol}-fundsペアです。コンテナ側のパスはcardano-cli(L1での署名と送信)に使用されます。ホスト側の同じ鍵のコピーは、PyCardanoによってヘッド内トランザクションのために読み取られます。同じレイアウトの別のデプロイメントを指すのは設定変更です。異なるトポロジーを指すのはそうではありません(制限事項を参照)。


テスト

.venv/bin/python test_ops.py           # offline — no devnet needed
.venv/bin/python test_ops_devnet.py    # live — needs a devnet with the head Idle

test_ops.py は、すべての状態変更ツールがrequires_confirmationを返し、confirm=Trueなしではクライアントに到達しないこと(スタブクライアントはコマンドがゲートを逃れた場合に例外を発生させる)、リクエストペイロードがAPIと一致すること、最小UTXO拒否とUTxO検証が機能すること、エラーテーブルが解析およびデコードされること、そして16個すべてのツールがサーバーに登録されることをアサートします。

test_ops_devnet.py は、実際のヘッドをライフサイクル全体で駆動し、各段階での観測可能性をアサートします:ゲートチェック → init → commit → 6つの読み取りツール → 2つのヘッド内支払い → デコミット(資金がL1に現れ、ヘッドが開いたままであることで検証) → close → fanout → Idleに戻る → ログとエラーデコード。devnetが起動していないか、ヘッドがIdleでない場合は、明確なメッセージとともにスキップします。


運用上の注意

ヘッドを失う前に知っておくべきこと。

H39 / FanoutUTxOHashMismatch はヘッドを永久にスタックさせます。 ファンアウトは閉じたヘッドがコミットしたものを再現できないため、ヘッドは決済できず、資金はスタックします。2つの原因があり、どちらも防止可能で、ここでは両方ともガードされています:2.3.0でのマルチUTXOデポジット、およびL1最小UTXO未満のヘッド出力。詳細はexplain_error("H39")を参照してください。

ヘッドは最小UTXOをゼロにしますが、L1はそうではありません。 0.5 ADAの出力はL2では問題なく取引されますが、L1では再作成できません。send_txはこの理由から1 ADA未満を拒否します。

デポジットはデポジット期間後に吸収されます、即座ではありません。commit_fundsは吸収が行われない場合に待機して報告します。着地しなかったデポジットはpending_depositsに表示され、recover_depositで回収されます。

クローズは一方的で、全員に影響します。 どの参加者でもクローズでき、ヘッド全体が決済されなければなりません。ゲートは主にこのために存在します。

ヘッドはすべての参加者がオンラインである必要があります。 支払いがハングした場合は、ツールを疑う前にdocker compose psを確認してください。

デモdevnetのブロックプロデューサーは、長時間のアイドル後に停止する可能性があります——cardano-cli query tipが同じスロットを2回返し、すべてがハングします。./reset_devnet.shで修正できます。devnetは設計上使い捨てです。

解析不能なWebSocket入力はtagを返しません。 ノードが認識しないコマンドは、タグ付きイベントではなく、裸の{"input", "reason"}オブジェクトとして返されます——APIを直接スクリプト化する場合に知っておく価値があります。タグ付きイベントを待つクライアントはハングするからです。ここでのクライアントはそれを処理します。


制限事項

partial_fanoutは2.3.0より新しいノードが必要です。 このコマンドはリリース後(hydra PR #2750、コミットa271cced2)であり、固定されたデモイメージはそれを拒否します——ノードは既知のコマンドをリストし、PartialFanoutはその中にありません。ツールはこれを正確に検出し、バージョンギャップを報告します。コードパスはmasterからビルドされたノードに対応していますが、その拒否までしかテストされていません。

手数料はゼロです。 tx_builder.pyはfee=0をハードコードしており、これはデモのプロトコルパラメータでは正しいですが、他の場所では間違っています。実際の手数料見積もりとコイン選択は、preview/preprodやmainnetを指す前に必要です。

Devnet形状の前提。 3つのパーティ、既知の鍵名、cardano-nodeコンテナ内で読み取り可能な鍵、L1クエリとログに利用可能なdocker compose。ヘッドAPI層は汎用的ですが、L1ヘルパーはそうではありません。

ADAのみ。 トランザクション構築は純粋なlovelace UTXOを処理します——ネイティブトークン、スクリプト、データム、ミントはありません。

recover_depositは実際にスタックしたデポジットに対してテストされていません。 APIに従っていますが、デモdevnetはデポジットを確実に吸収するため、オンデマンドで生成できません。

認証なし。 サーバーに到達できる人は誰でもヘッドを操作できます。これはローカルオペレーターツールには適切ですが、外部に公開されるものには適していません。


拡張

ツールの追加: 関連するtools/モジュールにok() / err() / needs_confirmation()を返すプレーンな関数を書き、server.pyに薄いラッパーを登録します。ツールモジュールはFastMCPをインポートしないため、テストから直接呼び出せます——両方のスイートがそれらを駆動する方法です。

プロトコルコマンドの追加: HydraClientに_command_and_wait(command, ok_tags)を使用するメソッドを追加します。これはコマンドを送信し、結果イベントを待ち、CommandFailedとタグなしのパース拒否の両方をエラーとして扱います。

別のデプロイメントをターゲットにする: NODESをエンドポイントに、HYDRA_DEMO_DIR / HYDRA_REPOを適切なパスに設定します。デモの3パーティレイアウトを超える場合は、cardano.pyとtx_builder.pyの鍵処理、および手数料を見直す必要があります。

参考文献

プロトコル自体についてはHydraドキュメントを、これらのツールを使用したヘッド操作のガイドツアーについてはRUNBOOK.mdを参照してください。

Related MCP Connectors

Related MCP Servers