ChatGPTで作ったテスト環境向けの指示を、MCP経由でVS CodeのCodexへ渡そうとしたところ、別の会話で保存したログ調査の指示が表示されました。コピー後のハッシュ照合は通っています。
原因はコピーの破損ではなく、複数の会話で共有していた「最新1件」から対象を決めていたことでした。
ChatGPTの会話を複数並行し、MCP経由でCodexへ指示を渡している人向けに、対象の選択と内容の照合を分けた自作連携の例を紹介します。
MCPが保存する指示ファイルを「原本」、Codexへ渡すために内容を固定したファイルを「固定コピー」と呼びます。原本とコピーの内容が一致しても、それだけでは送りたかった指示かどうかは分かりません。
ハッシュが通ったのは、別の指示を正しくコピーしたから
以前は、すべての会話が共通のinstruction-record.jsonを更新し、自作VS Code拡張「Reporter」がそのファイルを読んでいました。
次の順序になると、取り違えが起こります。A・Bは説明用の識別子です。
- 会話Aが「テスト環境の確認」を保存する。
- VS Codeへ添付する前に、会話Bが「ログ調査」を保存する。
- 拡張が最新原本Bを読み、Bの固定コピーを作る。
- 原本BとコピーBのハッシュ照合が通る。
- Aを送りたかった人に、Bの指示が表示される。
図の緑は処理、橙は人の操作、青は検証、赤は取り違えや停止を示します。
ハッシュで確かめていたのは、読み取った原本とコピーが一致することです。その原本が利用者の希望した依頼かどうかは、照合の対象に含まれていませんでした。
全文確認で気づけば送信は止められますが、そのたびに選び直す手戻りが生じます。見落とせば別の作業を依頼するおそれもあります。並行する作業が増える運用で、この検出を毎回人の注意に任せる状態を変えました。
これはMCP固有の問題ではありません。複数の依頼元が更新する共有データを、読み取り時点の「最新」だけで選ぶ連携にも当てはまります。
指示をID別に保存し、送りたいものを選ぶ
ローカルMCPサーバーに、指示IDごとの保存先を追加しました。
data/instructions/<submission_id>.json
レコードには指示ID、本文、本文のSHA-256、保存日時などを持たせます。同じIDがすでにある場合は、内容が完全一致するときだけ再利用し、不一致は拒否します。通常の保存処理で別の内容へ上書きしないためです。
既存経路との互換性を保つため、最新原本も残しています。
data/instruction-record.json
保存はID別原本を先に確定し、その後に最新原本へ反映する順序です。Reporterは両方を列挙し、同じIDがある場合はレコード全体のハッシュ一致を要求して重複を除きます。不一致なら候補表示前に停止します。
候補が1件でも、人間が選ぶ操作を省略しません。人が送信先のCodex会話と指示全文を確認して送る運用に合わせ、対象の選択も人間が行う構成にしました。
一覧では作業名・保存日時・IDを対応させる
候補一覧には、依頼元、作業名、対象VS Code、保存日時、指示IDを表示します。
選択中の行には「記事用スクリーンショット撮り直し」、その下には別の作業名が表示されています。同じ依頼元でも、作業名と保存日時・IDを組み合わせて区別できます。
この画像は候補一覧の表示例です。下段には作業名が「未取得」の過去の候補も残っています。選択中の指示は、ChatGPT側で作成するヘッダーの重複を取り除いたものです。
人間による選び間違いは残ります。同名の作業なら日時とIDも確かめ、添付前に全文を確認します。ここで減らしたのは、別の会話による更新で受渡し対象が黙って入れ替わる問題です。
選んだ後は、同じ内容から固定コピーを作る
選択と照合は別の役割です。選択では「どの依頼を送るか」を決め、再検証では「選んだときと同じ原本か」を確かめます。
選択した原本のpathと、読み取ったバイト列全体のSHA-256を保持します。受渡し作成時にそのpathから読み直し、レコード形式と本文ハッシュの整合、全体ハッシュの一致を検証します。
最小限の流れを擬似コードで示します。
候補を列挙する:
原本を読み、レコード形式と本文ハッシュの整合を検証する
pathと、読んだバイト列全体のSHA-256を候補に保持する
依頼元・作業名・保存日時・指示IDを一覧へ表示する
人間が候補を選んだら:
選んだ候補のpathと全体ハッシュを expected に保持する
expected.path から原本のバイト列を読み直す
読めない・形式不正・本文ハッシュ不一致なら停止する
バイト列全体のSHA-256が expected の値と違えば停止する
検証したバイト列から固定コピーと受渡し情報を作る
人間へ指示全文を表示する
確認された場合だけCodex入力欄へ添付する
実装では指示IDと本文ハッシュも個別に比較していますが、検証対象はレコード全体のハッシュと重なります。
再検証が特に必要なのは、選択後も更新される互換用の最新原本を選んだ場合です。ID別原本は通常の保存で上書きされませんが、消失や読取失敗の場合には同様に停止します。別の最新指示を代わりに使う処理は入れていません。
最新原本は保存のたびに更新されます。ただし、送りたい指示はID別原本から選べるため、最新原本に何が残るかだけで対象が決まることはありません。同時書込みや更新途中の停止時の挙動は、次の未確認事項に含めています。
確認できた範囲と制約
コード上では、ID別保存、候補の列挙と重複照合、選択後の再検証、不一致や読取失敗で停止する分岐を確認しました。関連する自動テストには過去の改修時に成功した記録があります。今回の記事編集では再実行していません。
人間の実操作では、過去の正常な受渡しで、一覧から指示を選び、Codexへ送り、結果の確認・承認後にChatGPT側で取得する往復を確認しています。今回掲載した画像は候補一覧の表示までを示すもので、選択中の指示の処理完了や承認済みを示す画像ではありません。
未確認なのは、同時書込みや更新途中の停止に対する保存の原子性、すべての更新タイミングでの動作、原本の長期蓄積管理です。読めない場合に停止することと、どの条件でも受渡しを継続できることは分けて評価します。
また、旧来の固定パスを直接読む経路には同じ問題が残り得ます。今回の対策が成立するのは、明示選択と選択後の照合を通る経路です。連携は利用中のCodex拡張の添付機能にも依存します。SHA-256による照合は暗号化や送信者認証を担いません。
内容が一致していることと、希望した対象であることは別の確認です。今回は、指示をIDで保存し、人が対象を選び、その選択を保持して受渡し時に照合することで、この二つを分けました。
関連記事
初期の接続と固定コピーによる受渡し構成はこちらにまとめています。
ChatGPTからVS CodeのCodexへコピペせずに指示を渡す――MCPで保存し、人間が確認して送信する構成
