ADR-0044: AI のエージェントからの操作は、WebMCP の API で登録し、MCP-B の polyfill と relay でも呼べるようにする¶
Status¶
承認
Context¶
AI のエージェントが、プレイグラウンドのツール(実行環境、キャンバス、スライドのエディタ)を操作できるようにする。
操作を公開する方式と、公開する操作の一覧を決める必要がある。
- どのツールも、トップレベルで開ける URL を持つ。isolation のヘッダは、Node の実行環境のパスにだけ返す。
- 実行環境は、WebContainers である。画面は、実行環境をインターフェースを通して使う。
- キャンバスは、draw.io の Embed Mode と Excalidraw の 2 つのツールである。
- スライドのエディタは、Marp で作る。
- Go のコードは、Yaegi のインタプリタの WASM で動かす。
- ADR-0037: Go のコードを、Yaegi のインタプリタの WASM で動かす
- SQL だけを動かすツールは、PGlite を Web Worker で動かす。SQL だけを動かすツールは、DB の中身を保存しない。Web Worker を作り直すと、DB は初期の状態に戻る。保存するのは、入力欄の SQL だけである。
- Go を動かすツールは、訪問者のファイルを IndexedDB に保存する。
- サイトは、AI のモデルを提供しない。利用者が、自分の AI のエージェントをつなぐ。サーバーで AI のエージェントを動かさない。
- WebMCP は、ページが、AI のエージェントが呼べる操作を登録する API である。API は
document.modelContextにあり、registerTool、getTools、executeToolを持つ。 - WebMCP は、Chrome 149 から origin trial である。出荷するバージョンは書かれていない。手元では、フラグ
chrome://flags/#enable-webmcp-testingで使える。- 「Join the WebMCP origin trial from Chrome 149.」
- 出典: Chrome: WebMCP
- 出典: Chrome Status: WebMCP
- Edge 150 は origin trial である。Firefox と Safari は、標準化への立場の issue だけがある。Claude が WebMCP の操作を直接呼べるという記述は、読んだ資料に無かった。
- MCP-B の
@mcp-b/globalは、document.modelContextの polyfill である。@mcp-b/webmcp-local-relayは、利用者の端末で動く MCP のサーバーで、ページと127.0.0.1の WebSocket でつなぎ、ページが登録した操作を MCP のクライアントに見せる。どちらも MIT である。
Decision¶
1. 登録する API¶
操作は、document.modelContext.registerTool で登録する。
登録のコードは、ツールごとに 1 か所に置き、ページが持つ操作(Markdown の読み書き、Excalidraw の API、draw.io への postMessage、実行環境のインターフェース)を呼ぶ。
WebMCP が使えるブラウザでは、登録した操作を、そのまま AI のエージェントが呼べる。
2. origin trial のトークン¶
本番に、origin trial のトークンを入れない。
期限のあるトークンを更新する運用を持たないためである。
- WebMCP が出荷されたブラウザと、フラグを有効にしたブラウザでは、登録した操作を呼べる。
- そのほかのブラウザでは、polyfill と relay を通して呼ぶ。
3. polyfill と relay¶
MCP-B の polyfill と、relay につなぐスクリプトを、自分のサイトから配る。
WebMCP の無いブラウザと、デスクトップの MCP のクライアントから呼べるようにするためである。
- 2 つのスクリプトは、AI からの操作の許可のスイッチがオンのときにだけ読み込む。スイッチのオンを保存してあるページでは、ページを開いたときに読み込む。
- 利用者は、自分の MCP のクライアントに relay を足す。ツールのページに、relay を起動するときに
--widget-origin https://play.fukuemon.devを渡すことを書く。
4. 公開する操作¶
公開する操作は、次の 23 個にする。
依存するツールができた順に、ツールごとに足す。
| ツール | 操作 | 注釈 |
|---|---|---|
| スライド | Markdown の全文、テーマ、枚数を返す | 読み取りだけ、信頼できない内容 |
| スライド | Markdown の全文を置き換える | 無い |
| スライド | テーマを変える | 無い |
| Excalidraw | 要素の一覧を返す | 読み取りだけ、信頼できない内容 |
| Excalidraw | 図形、文字、図形をつなぐ矢印を足す | 無い |
| Excalidraw | 指定した要素の位置、大きさ、色を変える | 無い |
| Excalidraw | 全ての要素を消す | 重大な操作 |
| draw.io | 図の XML を返す | 読み取りだけ、信頼できない内容 |
| draw.io | 図に mxCell を足す |
無い |
| draw.io | 図の全体を、渡した XML に置き換える | 重大な操作 |
| 実行環境 | ファイルの一覧を返す | 読み取りだけ |
| 実行環境 | ファイルの中身を返す | 読み取りだけ、信頼できない内容 |
| 実行環境 | ファイルを書く | 無い |
| 実行環境 | ファイルを消す | 重大な操作 |
| 実行環境 | コマンドを実行し、終了コードと出力を返す | 重大な操作、信頼できない内容 |
| SQL | 入力欄の SQL を返す | 読み取りだけ、信頼できない内容 |
| SQL | 渡した SQL を実行し、結果を返す | 重大な操作、信頼できない内容 |
| Go | ファイルの一覧を返す | 読み取りだけ |
| Go | ファイルの中身を返す | 読み取りだけ、信頼できない内容 |
| Go | ファイルを書く | 無い |
| Go | ファイルを消す | 重大な操作 |
| Go | ファイルを実行し、終了コードと出力を返す | 重大な操作、信頼できない内容 |
| Go | テストを実行し、終了コードと出力を返す | 重大な操作、信頼できない内容 |
「読み取りだけ」は readOnlyHint、「信頼できない内容」は untrustedContentHint、「重大な操作」は consequentialHint を指す。
「実行環境」は、Node の実行環境を指す。
- SQL だけを動かすツールの 2 つの操作は、入力欄の SQL を書き換えない。利用者が書いて保存した SQL が、エージェントの操作で消えないようにするためである。
- AI のエージェントが実行した SQL の結果は、操作の記録の欄にだけ出す。SQL の画面の結果の表は、変えない。利用者が見ている結果が、利用者の操作なしに変わらないようにするためである。
- テーブルとカラムの一覧を返す操作は、足さない。エージェントは、SQL を実行する操作で
information_schemaを読む。 - Go のファイルの 4 つの操作は、Node の実行環境の同じ操作と、名前の付け方と入出力をそろえる。エージェントが、同じ使い方で両方を操作できるようにするためである。
- Go を動かすツールは、ファイルをディレクトリに分けて持つ。ディレクトリを含むパスは、Node の実行環境の操作と同じに扱う。
- Go を実行する操作とテストを実行する操作は、Go を動かすツールが持つ今のファイルを実行する。入力でコードを受け取らない。画面の実行のボタンとテストのボタンと、同じファイルを実行するためである。
- Node の実行環境の DB には、SQL を実行する操作を足さない。エージェントは、ファイルを書く操作とコマンドを実行する操作で、
pgを使うコードを書いて実行できる。
5. 入力の検証と失敗の理由¶
操作は、受け取った入力を、操作の中で検証する。
ブラウザの executeTool が、inputSchema に合わない値を、そのまま操作に渡すためである。
失敗の理由は、例外を投げずに、結果の文字列で返す。
操作が例外を投げると、呼んだ側に、決まった文のエラーだけが届くためである。
Verification¶
端末は Apple M2、配信は localhost である。
ブラウザは、Google Chrome 156 と、Playwright 1.65.0 の Chromium 158、Firefox 159、WebKit 27.2 で、どれも headless である。
Chrome 156 と Chromium 158 は、フラグ enable-webmcp-testing を有効にした。
バージョンは、@mcp-b/global 6.2.0、@mcp-b/webmcp-local-relay 6.2.0、@modelcontextprotocol/sdk 1.32.2、@webcontainer/api 1.6.4 である。
| 呼び方 | 確認した環境 | 結果 |
|---|---|---|
ページの中の API(getTools と executeTool) |
Chrome 156 とフラグ | 15 個の全てを呼べた |
| 拡張機能の content script | Chromium 158 とフラグ | スライドの操作を一覧にして、Markdown を置き換えられた |
| MCP-B の relay と、MCP のクライアント(SDK) | Chrome 156、Chromium 158、Firefox 159、WebKit 27.2 | スライドの Markdown の読み書きを、4 つのブラウザで呼べた |
| 実物の AI のエージェント | 無い | 未確認 |
- フラグの無い Chrome 156 と Chromium 158 には、
document.modelContextが無かった。 - isolation のヘッダを返す実行環境のページでも、API を使えた。ファイルを書き、
node sum.jsを実行して出力と終了コードを受け取り、ファイルを消した。 - draw.io に図形を足す操作は、今の XML に
mxCellを足して、loadで読み込み直す形にした。全体を置き換える操作の後に図を読むと、渡した XML だけが返った。 - polyfill を読み込むと、Firefox と WebKit にも
document.modelContextができ、同じregisterToolのコードが動いた。 - relay を通して、スライドのテーマを変える操作を、Chrome 156 で呼べた。
enumに無い値は、relay が検証して「Input validation error」を返した。 - Chrome 156 は、フラグと polyfill の両方がある状態で、relay を通した呼び出しが通った。どちらの実装が登録を受けたかは未確認である。
- 通信を記録したのは、relay を通したスライドのページだけである。そのページからサイトのサーバーへのリクエストは、ページとスクリプトの取得だけだった。ほかのページの通信は記録していない。
- polyfill は brotli で 71 KB、relay につなぐスクリプトは合わせて 9 KB だった。外部へのリクエストは、
127.0.0.1への WebSocket だけだった。 - relay を、別の origin を渡した
--widget-originで起動すると、ページの接続は拒否された。relay のデフォルトは、どの origin のページも受け付ける。 - 別の origin の記事に埋め込んだ iframe は、
allow="tools"が無いと、登録を拒否された。 - 実物の AI のエージェント(Claude Desktop、Claude Code、ChatGPT Desktop、Gemini in Chrome)からは、呼んでいない。
- 本番の HTTPS のドメインから
127.0.0.1へ WebSocket を張るときの、Local Network Access の許可の出方は未確認である。 - origin trial の延長が承認されたかは、未確認である。
Considered Options¶
| 選択肢 | 強み | 弱み |
|---|---|---|
| 採用: WebMCP の API で登録し、トークンを入れず、MCP-B の polyfill と relay でも呼べるようにする | 登録のコードが 1 つで済む。WebMCP の無いブラウザと、デスクトップの MCP のクライアントから呼べる。トークンを更新する運用が要らない | フラグを有効にしていない Chrome の、ブラウザに組み込まれたエージェントからは呼べない。MCP-B に依る。利用者が、自分の端末で relay を起動する |
origin trial のトークンを、play.fukuemon.dev に入れる |
フラグなしの Chrome で、WebMCP の API が使える | 期限ごとにトークンを更新する。origin trial の間に API が変わると、訪問者に影響する |
| WebMCP だけにし、polyfill と relay を入れない | 第三者のライブラリに依らない | Firefox と Safari の訪問者と、デスクトップの MCP のクライアントからは呼べない |
| MCP-B を使わず、ローカルの MCP のサーバーと WebSocket の橋渡しを自分で作る | 第三者のライブラリに依らない | 作って保守する量が増える。試作していない |
| WebMCP の出荷まで、AI からの操作を作らない | 作り直しが出ない | 出荷の時期は、資料に無い |
| 読み取りと、全体の置き換えだけを公開する | 操作の数が少ない | 図形を 1 つ足すときも、AI のエージェントが図の全体を書き直す |
| draw.io とスライドに、1 つずつ変える操作と消す操作を足す | AI のエージェントが、小さい変更を小さい操作で行える | 操作の数が増える |
SQL だけを動かすツールと Go を動かすツールの操作の選択肢は次のとおり。
| 選択肢 | 強み | 弱み |
|---|---|---|
| 採用: SQL だけを動かすツールに 2 つ、Go を動かすツールに 6 つの操作を公開する | エージェントは、利用者の SQL を読み、直した SQL を実行できる。Go では、Node の実行環境と同じ使い方で、ファイルを書いて実行できる | 操作が 8 個増える。エージェントが直した SQL は、入力欄に入らない |
| SQL だけを動かすツールに、入力欄の SQL を置き換える操作も足す | エージェントが、直した SQL を入力欄に入れられる | 利用者が書いて保存した SQL を置き換える。確認を出す操作が増える |
| SQL だけを動かすツールは、SQL を実行する操作だけにする | 操作が少ない | エージェントが、利用者の書いた SQL を読めない |
| エージェントが実行した SQL の結果を、SQL の画面の結果の表にも出す | 利用者が、エージェントの実行の結果を、表で読める | 入力欄の SQL と、表の結果が、合わなくなる |
| Go のファイルを、1 回で全て返す操作にまとめ、消す操作を持たない | 操作が少ない | Node の実行環境と、形が違う。ファイルを消せない |
| Go を動かすツールのファイルを、ディレクトリに分けない形にする | 複数の package の import の解決が要らない | Node の実行環境の操作と、パスの扱いが違う。package を分けたコードを書けない |
| Node の実行環境の DB にも、SQL を実行する操作を足す | エージェントが、コードを書かずに SQL を実行できる | 操作が 1 つ増える。DB を使うプロジェクトのときだけ登録する処理が要る |
Consequences¶
Positive¶
- 利用者は、自分の AI のエージェントに頼んで、ファイルを書き、コードを実行し、図とスライドを編集できる。
- 利用者は、自分の AI のエージェントに頼んで、SQL を実行して結果を受け取り、Go のファイルを書いて実行し、テストの結果を受け取れる。
- 利用者が書いて保存した SQL は、エージェントの操作で変わらない。
- 操作の内容は、ブラウザの中か、利用者の端末の relay を通る。fukuemon.dev のサーバーに送る処理を持たない。
- WebMCP が出荷されたときに、登録のコードを変えずに済む見込みである。
Negative¶
- WebMCP は origin trial の段階で、API が変わることがある。変わったときに、登録のコードと polyfill のバージョンを直す。
- 利用者の AI のエージェントは、読み取った内容を、モデルの提供元に送ることがある。
- relay を通して呼ぶには、利用者が MCP のクライアントに relay を足す設定が要る。
- 実物の AI のエージェントから呼べることを、確認できていない。
- draw.io の図の一部だけを変える操作が無い。AI のエージェントは、全体を置き換える操作で変える。
- エージェントが実行した SQL は、DB の中身を変える。利用者が画面で見ている結果の表と、DB の中身が、合わなくなることがある。