Muse は Meta が提供する個人向けのエージェントで、仮想マシン(VM)に常駐して動きます。私は Muse を、Claude Code と同じように、VM の上で動くエージェントとして使いたいと考えています。文書を書かせ、コードを書かせ、分析までやらせて、その成果物を GitHub のリポジトリに履歴つきで置いていく使い方です。この記事は、その入口になる、成果物をリポジトリに置くところまでの記録です。
この記事は、Muse が自分の作業を記録したものをもとに、Claude Code に確認してもらいながら、私が書きました。数字は、リポジトリの履歴と突き合わせています。作業は 2026 年 11 月 2 日に行いました。
この記事で示すこと
Muse は、文書、コード、分析の結果、生成した画像と動画を作ります。この成果物を GitHub の非公開リポジトリに置き、履歴をつけて管理できるようにする方法を、試した順に書きます。コーディングエージェントが、作ったものを自分でリポジトリに置いていく形に近づける、というのがねらいです。
試した方式は 3 つで、うまくいったのは最後の 1 つです。
- 公式コネクタでは、テキスト文書だけが通りました
- SSH による git の push は、VM の外向きの通信が通らず失敗しました
- 個人用アクセストークンと Git Data API の組み合わせで、画像と動画を含む 160 ファイル、約 155 MB を登録できました
SSH が通らず、git の通常の操作が使えない環境でも、コミットを API で組み立てれば、成果物を履歴つきで管理できます。これがこの記事の中心です。通常の git が使える環境と同じことができるようになった、という話ではありません。clone や差分の確認は、この VM では行えていないままです。できなかったことは「残っている制約」に書きました。リポジトリを通じた Claude Code との往復は、その結果を専用の節に載せています。
用語
| 語 | 意味 |
|---|---|
| 仮想マシン(VM) | Muse が常駐して作業する、Meta が用意した計算環境 |
| コネクタ | Muse が外部のサービスに接続するための仕組み。公式のものと、利用者が作るカスタムのものがある |
| 個人用アクセストークン(PAT) | GitHub の API を呼ぶための合鍵。対象のリポジトリと権限を細かく絞って発行できる |
| Secure Vault | PAT などの資格情報を預ける保管場所。Muse の説明では、値は Muse からも見えない設計になっている |
| Git Data API | git の内部の部品(blob、tree、commit、ref)を、GitHub の API で直接作る仕組み |
| blob | ファイルの中身そのもの |
| tree | ディレクトリの構成。どのパスにどの blob があるかの対応表 |
| commit | ある時点の tree に、メッセージと親のコミットをつけたもの |
| ref | ブランチの先端が指しているコミット |
連携の目的と前提
目的は、Muse の成果物の置き場所を一か所に決めることです。Muse は VM に常駐して作業し、文書、調査結果、生成した画像と動画を作ります。これらが VM の作業領域に散らばったままだと、後から探す手間がかかります。そこで、作業ごとにディレクトリを切った非公開リポジトリを私が作り、そこへ集約することにしました。
前提として、Muse には通常の git の対話操作が保証されていません。シェルは使えますが、外向きの通信がどの経路で通るかは環境に依存します。この前提は、方法 2 の失敗で具体的な形になりました。
全体の構成
連携の全体像は次の図のとおりです。赤い経路が、最終的に採用した方法 3 です。
登場する要素は 4 つです。私、Muse、連携の仕組み、そして GitHub です。連携の仕組みの中には、公式 GitHub コネクタ、カスタムコネクタ、Secure Vault の 3 つが入ります。
方法 1 公式コネクタによるテキスト文書の登録
最初に試したのは、公式の GitHub コネクタです。私が OAuth による認可と GitHub アプリのインストールを行い、対象リポジトリとして成果物用のリポジトリだけを選びました。接続が確認できた時点で、Muse はリポジトリの中身の読み取りと、ファイルの登録ができるようになりました。
この経路で、チェックリストと分析文書を 1 本ずつ登録しました。どちらも Markdown のテキスト文書で、作業ごとのディレクトリに分けて置きました。
公式コネクタのファイル登録は、スナップショットと呼ばれる一括登録の仕組みで動いていました。Muse が確認できた制約は次のとおりです。
- 送れるのは UTF-8 のテキストファイルだけである
- 1 回の登録は最大で 2,000 ファイル、合計 16 MiB までである
- バイナリファイルが 1 つでも混ざると、その回の登録全体が失敗する
- 削除はできない
テキスト文書の管理だけなら、この経路で足ります。ただし今回の目的には、生成した画像と動画が含まれます。バイナリも含めて管理する方針に決めたため、別の経路が必要になりました。
方法 2 SSH による git push と、その失敗
通常の git が使えるなら、VM にリポジトリを複製し、commit と push を Muse が行えます。認証にはデプロイキー(特定のリポジトリだけに使える鍵)を使うことにし、Muse の公開鍵を、私がリポジトリのデプロイキーとして登録しました。書き込みの許可も付けました。
結果は、接続自体が成立しませんでした。鍵の認証まで到達する前に、接続がリセットされました。Muse は経路を変えて、同じ確認を繰り返しています。
- SSH の 22 番ポート宛ては、接続がリセットされる
- 443 番ポートを使う ssh.github.com 宛てでも、同じく認証の前にリセットされる
- HTTPS では GitHub のサイトと API に到達できる
鍵の登録内容ではなく、この VM から外へ出る SSH の通信が通っていない、という切り分けです。通らない原因が、VM のネットワークの設定にあるのか、その外側にあるのかまでは分かりません。デプロイキーの経路は、設定が正しくても使えません。このデプロイキーは不要になったので、削除してよいと Muse から伝えられました。
方法 3 PAT と Git Data API による登録
PAT の発行条件
残った経路は HTTPS です。HTTPS で git の操作をするには資格情報が要ります。そこで、権限を細かく絞れる個人用アクセストークン(PAT)を、この連携専用に発行しました。条件は次のとおりです。
- 対象リポジトリは、発行の時点では、成果物用のリポジトリだけ
- 権限は Contents(リポジトリの中身)の読み書きだけ
- メタデータの読み取りは、この方式のトークンの必須の権限として自動で付く
- 有効期限は 1 年
GitHub の説明では、この方式のトークンにも期限なしを選べます。ただし、組織の管理者が最大の有効期間を決めている場合は、期限なしが使えないことがあります。今回は期限を切る設定にしました。
トークンの受け渡し
トークンの値は、チャットでは Muse に渡していません。私が Secure Vault の入力画面から登録しました。値は保管庫に直接入り、Muse は値そのものを見ることができません。
受け渡しの後段は、カスタムコネクタの仕組みを使いました。Muse はカスタムコネクタとして、この PAT を使う接続を登録し、実行役のスクリプトは、資格情報の代理(本物の値の代わりに付ける目印)をリクエストに付けます。送信先が api.github.com のときに限り、実行基盤が代理を本物のトークンと交換する、というのが Muse の説明です。スクリプトの中にも、コマンドの引数にも、トークンの値は現れません。この交換は実行基盤の設計で、外から確かめる手段は、記録にはありません。
Git Data API による push の手順
git のプロトコルは使いません。代わりに、Git Data API で、コミットを部品から組み立てました。手順は 4 段です。
- 現在のブランチの先端(ref)を取得し、そのコミットとツリーを引く
- 対象ファイルごとに blob を作る。中身は Base64 で送るため、バイナリもそのまま入る
- パスと blob の対応を集めて tree を作る
- commit を作り、ref をそのコミットへ更新する
呼び出しの順序は次の図のとおりです。
概念レベルの擬似コードは次のとおりです。
# 資格情報は実行役が Vault と交換して付ける。値自体はここに現れない
head = api.get("/repos/{owner}/{repo}/git/ref/heads/main")
tree = head のコミットが指す tree
追加分 = []
for file in 対象ファイル:
blob = api.post("/git/blobs", {
"content": base64(file の中身),
"encoding": "base64",
})
追加分.append({"path": file のパス, "mode": "100644",
"type": "blob", "sha": blob の sha})
new_tree = api.post("/git/trees", {"base_tree": tree, "tree": 追加分})
new_commit = api.post("/git/commits", {"message": 記録文,
"tree": new_tree, "parents": [head]})
api.patch("/git/refs/heads/main", {"sha": new_commit})
ref の更新が最後の 1 回だけなので、途中で失敗してもリポジトリの見える状態は変わりません。失敗した回は、blob が残るだけで済みます。
実行結果
最初に、WebP 画像 1 件だけでテストの登録を行い、成功しました。その後、本番の一括登録を行いました。リポジトリの履歴と突き合わせた結果は次のとおりです。
- 一括登録は 159 ファイルで、テストの 1 ファイルを足すと 160 件になる
- 登録直後のリポジトリ全体は 165 ファイル、約 154.6 MB で、約 155 MB という規模に合う
- 最大のファイルは 13,024,077 バイト(約 13 MB の動画)で、決めておいた 1 ファイル 50 MB までの条件に収まった
- 一括登録 159 ファイルのうち、133 件が生成した画像と動画、7 件が試作のコード(HTML と Python のスクリプトと、その出力画像)で、残りは文書などである
- 登録先は、作業の種類ごとのディレクトリに分けた
- push の実行は、その都度、私が承認した
方法 1 で登録したテキスト文書と合わせて、成果物が 1 つのリポジトリにそろいました。
3 方式の比較
試した 3 方式を並べると、制約の出どころが分かれます。
| 方式 | 送れるもの | 結果 | 詰まった場所 |
|---|---|---|---|
| 公式コネクタ | テキストだけ(2,000 ファイル、16 MiB まで) | 文書は登録できた | コネクタの仕様 |
| SSH とデプロイキー | 本来はすべて | 接続できなかった | VM から外への通信 |
| PAT と Git Data API | テキストとバイナリ | 160 件、約 155 MB を登録できた | 詰まらなかった |
セキュリティの設計
PAT の経路で守った条件は、権限の範囲と、値の扱いの 2 つです。
権限の範囲は、発行の時点では、対象を 1 リポジトリに絞りました。このトークンが漏れても、届くのは対象に選んだリポジトリだけです。有効期限は 1 年で、期限が切れれば登録は認証エラーになり、その時点で再発行が必要だと分かります。失効も GitHub 側の操作だけででき、Muse の側の作業は要りません。
値の扱いは、保管庫への直接登録と、代理による交換の 2 段にしました。トークンの値はチャット、スクリプト、コマンドの引数、ログのどこにも現れません。Muse 自身も値を見ていないので、Muse の記録や出力から漏れる経路がありません。
git の通常の操作はどこまで使えるか
SSH が通らないので、git の通常の操作がどこまで使えるかを、HTTPS でも確かめました。Muse に実際のコマンドと出力を見せてもらった結果は次のとおりです。
| 試したこと | 結果 |
|---|---|
| SSH(22 番、443 番) | 認証の前に接続がリセットされる |
| HTTPS で公開リポジトリを clone | 成功した(約 113 秒) |
| HTTPS で非公開リポジトリを clone | 認証で失敗した(could not read Username for 'https://github.com': terminal prompts disabled) |
| push | 試していない |
非公開リポジトリで失敗したのは、資格情報の代理の交換が api.github.com 宛てに限られていて、git の通信には資格情報が付かないためだと考えています。ただし、この理由は確かめていません。非公開リポジトリには、PAT と API で読み書きしています。
リポジトリを通じた Claude Code との往復
登録した成果物が、リポジトリを通じて Claude Code と Muse のあいだで行き来するかを、履歴から調べました。時刻はすべて協定世界時です。
Muse から Claude Code の側へ
この向きは、すでに動いていました。
- Muse は専用のディレクトリに、自分のトークン使用量の日次ログ(CSV)と、Claude Code 宛ての手紙を置いている。手紙には、集計に使う行の条件と、入力トークンの数え方の注意が書かれている
- 使用量の集計ツールの読み取りコードが、この CSV を読んでいる。手紙と同じ扱い(確定した行だけを数える、入力は新規入力ではなくキャッシュ読み込みを含む総量なので、Claude の合計とは足さない)になっている。Muse が CSV を置いた 7 分後に、集計ツールへ Muse の使用量の表示を足すコミットがあった
- 登録済みのファイルを、後から更新するコミットが 3 回ある。たとえば、分析文書は、登録の 40 分後にアイコンを足して更新されている
Claude Code から Muse へ
こちらの向きは、履歴に記録が無かったので、試験をしました。Claude Code が確認用のファイルを専用のディレクトリに置き(07:42)、私が Muse に読むよう頼み、Muse が返事のファイルを別の専用のディレクトリに登録(07:44)しました。ファイルには、その場で作った合言葉と、5 つの数の合計を求める小さな作業を書きました。
返事の内容を、履歴と突き合わせた結果は次のとおりです。
| 確かめた点 | 結果 |
|---|---|
| 合言葉 | 一致した |
| ファイルを最後に変更したコミットの SHA | 一致した |
| ファイルの blob の SHA | 履歴の値と一致した |
| 5 つの数の合計と最大値 | 191 と 91 で一致した |
| 返事のファイル | Claude Code が gh で読めた |
Muse の答えでは、公式コネクタと、PAT による API の両方で読め、内容は同一でした。どちらの経路で読んだかは Muse の申告で、履歴からは確かめられません。合言葉は、ファイルを読まないと書けない値なので、読んだこと自体は確かめられています。
もう 1 つ、Muse は、公式コネクタの読み取り結果に対して、自動の安全確認の通知が 1 件出たと答えています。そのため、実行の根拠を、私の依頼と、もう一方の経路で読んだ内容に置いたそうです。通知の中身と原因は確認できていません。ファイルに指示を書いたことが関係した可能性はありますが、推測です。
この試験で言えるのは、Claude Code から Muse へ、Muse から Claude Code へ、GitHub のリポジトリを通じて、1 往復が成立したことです。範囲は限られています。
- 試したのは、小さな 1 つのテキストファイルだけである
- 依頼は私が Muse に手で出した。Muse がリポジトリを自分で見に行って動くかは、試していない
- 所要は、Claude Code のコミットから返事のコミットまでが約 2 分だったが、人が仲介しているので、反応の速さの指標にはならない
- 画像や大きなファイルを Claude Code 側から渡した場合は、試していない
残っている制約
動くようになった範囲と、残っている制約は分けて書きます。
- 公式コネクタのスナップショット登録には、削除の手段がない。古いファイルの整理は、方法 3 のツリー操作でなら可能だが、まだ実施していない
- PAT には 1 年の有効期限がある。期限切れのたびに、私の再発行と再登録が要る
- clone、差分の確認、対話的な競合の解消といった git の通常の操作は、この VM では行えていない。SSH が通らず、HTTPS でも非公開リポジトリは認証で止まる。今回の登録は API で代替できたが、大きな競合の解消のような作業は代替しにくい
- Git Data API の blob は 1 件ごとに送るため、ファイル数が増えると登録の回数がそのまま増える。今回の 160 件では問題にならなかったが、数千件の規模で破綻するかは、試していない
- API でコミットを作る方式では、コミットの作者はトークンの持ち主の名義になる。Muse の作業と私の作業は、作者では区別できず、メッセージと時刻でしか見分けられない
作業の分担
この連携で、私と Muse が担った部分は次のとおりです。
| 作業 | 担った側 |
|---|---|
| 成果物用のリポジトリの作成 | 私 |
| 公式コネクタの認可、アプリの対象リポジトリの選択 | 私 |
| テキスト文書の登録(方法 1) | Muse |
| デプロイキーの登録 | 私 |
| SSH の疎通確認と切り分け | Muse |
| PAT の発行と権限、有効期限の設定 | 私 |
| PAT の Secure Vault への登録 | 私 |
| カスタムコネクタと実行役の作成 | Muse |
| push の承認(毎回) | 私 |
| 一括登録の実行(方法 3) | Muse |
教訓
今回の作業で残った教訓は 3 つです。
1 つ目は、制約の出どころを分けて考えることです。方法 1 の詰まりはコネクタの仕様で、方法 2 の詰まりは外向きの通信の制約でした。2 つを混ぜて考えると、SSH の失敗をコネクタの不具合と誤診したり、コネクタの上限を回線の問題と思ったりします。切り分けの材料は単純で、同じ相手へ別の経路(HTTPS)で届くかどうかでした。
2 つ目は、git のプロトコルと、コミットという単位は、分けられることです。プロトコルが通らなくても、コミットは blob、tree、commit、ref という部品の組み合わせでしかありません。部品を API で組み立てれば、履歴に残る形の登録は再現できました。エージェントに成果物の管理をさせる場合、git のコマンドが使えるかではなく、コミットを作る手段が用意されているかを見るほうが、実態に近くなります。
3 つ目は、エージェントの自己申告を、実行したコマンドと出力で確かめることです。HTTPS の clone について、Muse は最初「できた」と答えました。コマンドと出力を求めたところ、非公開リポジトリの clone は失敗していて、ファイルは API 経由で取得したものだったと訂正されました。自己申告は、確かめる前の仮の答えとして扱うのが安全です。
おわりに
この記事で示したのは、SSH が通らず、git の通常の操作が使えない環境でも、コミットを API で組み立てれば、文書だけでなくコードや画像まで、履歴のある形でリポジトリに置けることです。エージェントと GitHub の組み合わせは、メモや文書の置き場として使う例が多い印象でしたが、件数を数えたわけではなく、あくまで印象です。
Claude Code と同じ条件で使えているわけではありません。clone、差分の確認、競合の解消は使えておらず、Claude Code との往復も、小さなテキストファイル 1 つで確かめた段階です。大きなファイルや、Muse がリポジトリを自分で見に行って動く場合は、まだ試していません。同じ状況に置かれた人が、3 つの方式のうちどこで止まるかを先に知るための材料として、読んでもらえればと思います。

