ブラウザで動くオープンソースの動画エディタ「Timeline Studio」に、WebMCP経由でAIエージェントが操作できる機能を追加しました。v1.0.8では、プロジェクトの確認、編集案のプレビュー、適用、取り消し、編集可能なプロジェクトの保存までを扱います。
この記事では、ツールの登録方法に加えて、実装時に重要だった状態管理とUndoの設計を紹介します。
なぜ動画エディタにWebMCPを入れるのか
例えば、次の依頼を考えます。
2番目の動画を先頭に移動し、元動画の1〜5秒だけを残してから、1番目の動画につなげてください。
画面操作で実行する場合、エージェントはクリップの位置、ドラッグ可能な領域、タイムラインの拡大率などを判断する必要があります。一方、アプリケーション内部には、クリップID、素材の使用範囲、トラックのロック状態といった情報がすでにあります。
そこで、編集意図を構造化した入力として受け取り、アプリケーション側のルールで検証・実行できるようにしました。
WebMCPは、Webページがツールの説明、入力スキーマ、実行処理をブラウザに登録するための実験的な仕組みです。現時点の仕様はCommunity Group Draftであり、正式なW3C標準ではありません。すべてのブラウザやエージェントで動くことも前提にしていません。仕様草案
今回公開した9つのツール
| ツール | 役割 |
|---|---|
timeline_project_inspect |
プロジェクト概要と状態トークンを取得 |
timeline_track_inspect |
指定トラックをページ単位で取得 |
timeline_clip_inspect |
クリップの属性・時間範囲を確認 |
timeline_transcript_inspect |
保存済みの字幕と時刻を取得 |
timeline_edit_preview |
編集案を検証し、差分を表示 |
timeline_edit_apply |
プレビュー済みの編集案を適用 |
timeline_preview_seek |
再生ヘッドを指定時刻に移動 |
timeline_edit_undo |
条件を満たす直前のエージェント編集を取り消し |
timeline_project_save |
編集可能な.timelineファイルをダウンロード |
初期実装の編集対象は、メイントラックに存在するクリップの並べ替えと、条件を満たす通常速度の動画のトリミングです。トリミング可否はtrimAllowedで確認します。素材の追加・削除・複製や複雑な加工は、このツール群の対象にしていません。
また、字幕取得は音声認識を実行する機能ではなく、プロジェクト保存も動画のレンダリングではありません。
inspect → preview → apply
先ほどの例で、Aが4秒(元動画の0〜4秒)、Bが6秒(元動画の0〜6秒)のプロジェクトを編集します。まずプロジェクトとメイントラックを確認し、現在のstateTokenとクリップIDを取得します。
次に、timeline_edit_previewへ以下の形で計画を渡します。IDとトークンは説明用のプレースホルダーです。
{
"stateToken": "<inspectで取得した値>",
"summary": "Bを先頭に移動し、元動画の1〜5秒を使用する",
"clips": [
{ "clipId": "clip-b", "sourceIn": 1, "sourceOut": 5 },
{ "clipId": "clip-a" }
]
}
sourceInとsourceOutは、タイムライン上の位置ではなく、元のメディア内の絶対時刻を秒で指定します。現在使用している範囲内で両方を指定し、省略した場合は既存のソース時間マッピングを維持します。
clipsには既存のメインクリップを漏れなく、重複なく含めます。省略を削除として扱う仕様にはしていません。
プレビューでは差分とpreviewIdを返し、同じ内容をエディタにも表示します。この時点ではタイムラインは変更されません。適用時に渡すのは、返されたpreviewIdだけです。
{ "previewId": "<previewで取得した値>" }
利用者が画面から適用することも、利用者が許可した範囲内でエージェントが適用することもできます。
状態トークンに何を含めるか
エージェントがプロジェクトを確認した後、人が手動でクリップを動かすことがあります。古い状態を前提とした編集をそのまま適用しないよう、プレビューと適用の前に状態を検証します。
実装では、プロジェクトの編集内容、メディアの同一性、編集履歴の変化などからフィンガープリントを作り、不透明なstateTokenと対応付けています。既存のコマンドエンジンのリビジョン番号だけには依存しません。
一方、サムネイルのバックグラウンド更新やタイムラインの拡大率まで含めると、編集内容が変わっていないのに計画が失効します。そこで、編集結果に影響する状態と、表示・キャッシュの状態を区別しました。
保存処理も非同期です。アーカイブ作成後、ダウンロードを開始する前に状態を再確認しています。
Undoと再試行を安全に扱う
適用結果にはtransactionIdを返します。ツール経由のUndoは、そのトランザクションに対応する編集後の状態が現在も維持されている場合に限って実行します。後から利用者が行った編集を、古いスナップショットで上書きしないためです。
また、直前に適用済みのpreviewIdへの再試行は実行済みとして応答し、同じ編集を二重に適用しません。ドラッグなどの操作中も、競合する編集要求を受け付けないようにしました。
実際の変更は既存の編集エンジンと履歴機構を通します。WebMCP側に別のタイムライン編集ロジックを作らず、リップル編集、字幕と音声の関連、トラックロックなどのルールを共有する構成です。
登録のライフサイクルとデータの範囲
登録には現在のdocument.modelContext.registerTool(tool, { signal })を優先し、AbortControllerでコンポーネントの生存期間に対応付けています。既存の旧ネイティブ実装向けにnavigator.modelContextも検出しますが、未対応環境でAPIを擬似実装することはしていません。Chrome公式ドキュメント
ReactのStrictModeや言語切り替えでは、登録と解除が競合しないようにする必要があります。また、登録成功だけでは、個々のエージェントが発見・実行できることまでは保証できません。
読み取りツールが返すのは必要なメタデータと字幕テキストで、メディアのバイト列ではありません。ただし、それらの結果はエージェントのホストへ渡ります。「ブラウザ内で編集するので、すべての情報が端末外へ出ない」とは説明できません。字幕やクリップ名も、ツール利用の指示ではなく利用者由来のデータとして扱います。
検証結果と今後
実ブラウザでは、前述の例で「10秒 → 並べ替え・トリミング後8秒 → Undoで10秒」を確認しました。メディアの終端を超える範囲マーカーも保持し、そのマーカーが動画の長さを延ばさないことを確認しています。プロジェクト保存、ビルド、GitHub CIも通過しています。
これは基本的な編集の往復を確認した結果であり、画面操作に対する速度向上率や成功率を測定したものではありません。
今回重要だったのは、ツールを公開した後の契約を明確にすることでした。何を変更できるか、どの状態なら適用できるか、再試行やUndoをどう扱うか。この整理は、エージェントと人が同じアプリを操作するうえで役立つと考えています。