「連結しました」は嘘だった

動画生成MCP(Windows機のvideo-genに置いたMCPサーバー)に、2つの機能を足した。**同じ人物を複数の場面で同じ顔にする「登録済みキャラクター」**と、**完成した動画を1本につなぐjoin_videos**だ。miniに常駐させたエージェント(Mini)から、実際に3場面の動画を撮って1本につなげさせる通しテストをした。
1回目の依頼で、Miniは「3本を1本に連結して保存しました」と報告した。実際には、3本の動画を別々に保存しただけだった。join_videosは一度も呼ばれていなかった。
まず機能そのものの話
登録済みキャラクター
同じ人物を別の場面でも同じ顔で出したい、という要望への対応だ。参照画像を使う仕組み自体(ref2va)は前からあったが、エージェントに画像ファイル名を直接渡させるのは避けたかった。
- 許可リスト式にした。エージェントは
mioのような名前だけを渡す。画像ファイル名(char_mio.png)や外見の説明文は、管理者が書くJSON(VIDEO_MCP_CHARACTERS_FILE)にしか存在しない - 呼ぶたびにファイルを読み直す。人物を追加しても、MCPサーバーの再起動は要らない
- サーバー側がプロンプトの先頭に
<Picture 1>と外見の説明を自動で結びつける。エージェントに<Picture 1>という記法そのものを書かせようとすると、抜けて崩れることが分かったので、書かせないことにした
// characters.ts の許可リストの検証。名前は英小文字始まりの32文字以内、
// 画像名は char_ で始まりフォルダ指定を含まないものだけを通す
export const CHARACTER_NAME_RE = /^[a-z][a-z0-9_-]{0,31}$/;
export const CHARACTER_IMAGE_RE = /^char_[a-z0-9_-]{1,40}\.(png|jpg|jpeg|webp)$/;
export const MAX_CHARACTERS = 2;
実機では、mio(黒髪ボブ・眼鏡・トレンチコート)を指定した3場面すべてで同じ人物になった。参照なしでは別人が出る。
動画を1本につなぐ join_videos
それまでMiniが呼べたのは状態確認・生成・ダウンロードの3ツールだけで、結合・切り取り・字幕は呼べなかった。コンテナにffmpegも入っていない。
join_videosは、完了ジョブのID(jobIdかjobId:番号)を配列で受け取り、指定順に1本へつなぐ。サイズ・fps・音声の有無がクリップごとに違っても、先頭のクリップに自動でそろえる。
// join.ts: 先頭のサイズに合わせてscale+pad、24fps・48kHzへ統一。
// 音声が無いクリップには、同じ長さの無音を足してからconcatする
const w = even(clips[0].width);
const h = even(clips[0].height);
const anyAudio = clips.some(c => c.hasAudio);
// clips[i].hasAudio が false なら aevalsrc で無音トラックを生成して結合
結果は新しいjobIdの完了ジョブとして返る。既存の保存コマンド・ダウンロード口がそのまま使える。BGM・字幕・切り取りは付けなかった。H3が音声つきで、今のところ不要という判断だ。
「連結しました」は、なぜ嘘になったか
ここからが今日の本題。3場面(mioを指定、H3プリセット)を生成させたあと、Miniに「1本につなげて」と頼んだ。
openclawのmcp.servers.video-gen.toolFilter.includeにjoin_videosを足し、設定ファイルは更新済みだった。それでもMiniの応答は「連結して保存しました」という嘘の完了報告だった。実際に置かれていたファイルを見ると、3本の動画が別々の名前で保存されているだけだった。
調べると、Miniが持っているMCPクライアントの接続は、起動時に一度ツール一覧を取得したまま保持する作りだった。設定ファイルを書き換えても、その接続は作り直されない。join_videosはMiniから見えていない状態のまま、Miniは「それらしい」応答を返していた。
openclaw mcp reloadでMCPの実行環境を明示的に作り直すと、probe(ツール一覧の取得)が4ツールを返すようになった。その状態で同じ依頼をやり直すと、今度は実際にjoin_videosが呼ばれ、15.5秒・音声つきの1本が保存された。ダウンロードしたファイルのsha256を、Windows側で生成した元ファイルのsha256と突き合わせて一致を確認した。

2回目のテストで見つかった副作用
reload後の2回目のテストでも、1つ見つかった。mioは女性のキャラクターとして登録したが、2場面目でMiniが書いた説明文がA man ...になっていた。この場面だけ、画面の下に文字のようなものが焼き込まれた(H3は日本語の指示文をセリフと区別できず読み上げる・焼き込む既知の挙動がある)。
エージェントは、登録した人物の性別・外見を取り違えることがある。ツールの説明に「登録された外見の説明に合わせて書く」という一文を足した。
分かったこと
| 確認したこと | 結果 |
|---|---|
| 許可リストにツールを追加しただけで、稼働中のエージェントに反映されるか | されない |
openclaw mcp reload後、probeは新しいツールを返すか | 返す(4ツール) |
reload後、実際にjoin_videosが呼ばれ、結果ファイルは元と一致するか | 一致(sha256) |
| 登録キャラクターは、別の場面でも同じ顔で出るか | 出る(3場面とも同一人物) |
| エージェントは登録した人物の性別を正しく扱うか | 取り違えることがある |
最初の「連結して保存しました」という報告を、そのまま信じて次の作業に進んでいたら、3本バラバラのファイルを1本として扱い続けるところだった。エージェントの完了報告は、実行の証拠にならない。 同じ型の失敗は、検査スクリプトが「指摘なし」と表示しながら中身を見ていなかった件(ガードが素通りする書き方)でも起きている。今回は検査ではなくエージェント自身の自己申告だったが、「表示を信じず、実物(ファイル・ハッシュ)を確認する」という直し方は同じだった。
MCPサーバーにツールを足したら、openclaw mcp reloadを実行する。 手順はMCPツール反映漏れに置いた。
このMCPサーバーも、MiniがちょうぼっちやValscopeなどの実装を任されているのと同じ常駐エージェント基盤の上で動いている。