🧰 WebMCP DraftとChrome OT実装の差分を踏まえた実装・運用ガイド
目次

🧰 WebMCP DraftとChrome OT実装の差分を踏まえた実装・運用ガイド

WebMCPは、Webページの機能をAIエージェントが呼び出せるstructured toolとして公開するブラウザAPIです。
DOMを推測してクリックする従来のブラウザ自動化とは異なり、サイト側がJavaScript関数やHTMLフォームの目的、引数、実行方法を明示します。

この記事では、2026年8月26日時点のWebMCP Draft Community Group ReportとChrome 149〜156のOrigin Trial実装を基に、両者のAPI型の差、クロスDocument実行モデル、導入方法、運用とトラブルシューティングを整理します。
WebMCPを試したいフロントエンド開発者と、エージェント対応Webアプリの設計を検討するアーキテクトを対象にしています。

WebMCPの概念や設計思想だけでなく、Chrome OTでそのまま動かすための引数変換と、実環境で詰まりやすい箇所を扱う実装リファレンスです。

記事の全体像
この記事の全体像。以下、順に解説します。

概要

WebMCPは、ページがJavaScript関数やHTMLフォームを、自然言語の説明とJSON Schema付きのtoolとしてAIエージェントへ公開する仕組みです。
ページ、利用者、エージェントは同じ閲覧コンテキストを共有するため、既存のCookieセッション、画面状態、クライアント側ロジックを再利用できます。

現在の正規エントリポイントはdocument.modelContextです。
初期案のnavigator.modelContextは、同じWindowでDocumentが入れ替わったときにtool mapが残る問題を避けるためDocumentスコープへ移され、Chromium 150でdeprecatedになりました。

partial interface Document {
  [SecureContext, SameObject] readonly attribute ModelContext modelContext;
};

[Exposed=Window, SecureContext]
interface ModelContext : EventTarget {
  Promise<undefined> registerTool(ModelContextTool tool, optional ModelContextRegisterToolOptions options = {});
  Promise<sequence<RegisteredTool>> getTools(optional ModelContextGetToolOptions options = {});
  Promise<DOMString> executeTool(RegisteredTool tool, optional object inputObject = {}, optional ModelContextExecuteToolOptions options = {});
  attribute EventHandler ontoolchange;
};

仕様文書はW3C Web Machine Learning Community GroupのDraft Community Group Reportであり、W3C勧告ではありません。
Chromeは149から156までOrigin Trialを実施し、157での出荷を提案しています。
Edgeは150でOrigin Trialを実施しています。
一方、Mozillaのstandards positionはneutral、WebKitはopposeであり、クロスブラウザで安定利用できる段階ではありません。

バックエンドMCPやブラウザ自動化との違い

WebMCPはMCPとtool、schema、argumentsという語彙を共有しますが、実行場所と信頼境界が異なります。

方式 実行場所 強み 主な用途
WebMCP 開いているページのDocument UI、セッション、クライアント状態を再利用 共同編集、予約、購入、フォーム入力
バックエンドMCP サーバやローカルプロセス タブに依存せず継続稼働 サーバ能力、データ処理、外部システム連携
OpenAPI HTTPバックエンド 成熟したREST契約 UIを経由しないAPI操作
DOM・画面自動化 ブラウザ操作層 サイト側の対応が不要 tool未公開の画面操作

WebMCPの中心用途は、人間が同じUIで結果を確認できるhuman-in-the-loopです。
ヘッドレスブラウジングや、人間の監督がない完全自律ワークフローは設計上の主対象ではありません。

特徴

主な特徴は次のとおりです。

  • document.modelContextをDocument単位で公開
  • registerToolによる命令形tool登録
  • HTML formからtoolを合成する宣言形API
  • ページ内エージェント向けのgetToolsとexecuteTool
  • 動的な登録・解除を通知するtoolchange
  • AbortSignalによる登録解除と実行キャンセル
  • JSON Schemaによる入力契約
  • Permissions Policy toolsによるiframe制御
  • exposedToとfromOriginsによるクロスオリジン公開
  • readOnlyHintとuntrustedContentHintによる安全性ヒント
  • SecureContextとorigin-keyed agent clusterの要求

サイトは、曖昧なDOM構造をエージェントに解釈させる代わりに「何ができるか」を明示できます。
ただし、JSON Schemaは現時点でブラウザによる厳格な入力検証を保証しません。
toolのexecute内とサーバ側で、認可とバリデーションを必ず行う必要があります。

構造

WebMCPの構造を、システムコンテキスト、ユーザーエージェント内のコンテナ、実装コンポーネントの3段階で見ていきます。

システムコンテキスト図

WebMCPはページとブラウザエージェントの間に位置します。
バックエンドMCPは競合ではなく、サーバ側能力を補う別レイヤーです。

ブラウザエージェント 仲介として利用し得る 利用者 サイト作者 WebMCP ページ ページ内エージェント AIプラットフォーム バックエンドMCP 支援技術 内蔵エージェント 拡張ホストエージェント

ページ内エージェントはWebMCPのJavaScript APIを直接呼びます。
ブラウザ内蔵または拡張ホストのエージェントは、ページJavaScriptのgetToolsとは別のユーザーエージェント内部経路からtoolを観察します。
WebMCPは人間向けUIを置き換えず、同じページ上の操作経路を追加します。

コンテナ図

ユーザーエージェント内部では、セキュリティゲート、Documentに結び付くModelContext、Traversable単位の実行仲介、ブラウザエージェント向け観察経路に分けて考えられます。

ユーザーエージェント 推論 サーバ側能力 サーバ側ツール ツール公開 発見と実行 観察要求 観察要求 実装定義の公開 許可判定 許可判定 in parallel toolchange 配送 実行追跡 tool map 収集 ページ ページ内エージェント ブラウザエージェント 拡張ホストエージェント AIプラットフォーム バックエンドMCP セキュリティゲート Document ModelContext Traversable 実行仲介 観察経路

ModelContextはDocument生成時に1対1で関連付けられ、tool mapとローカルの実行中mapを持ちます。
Traversable実行仲介は、同じトップレベル閲覧コンテキストに属するDocument間の実行を追跡します。
別のトップレベルDocumentに属するtoolをexecuteToolで呼ぶとUnknownErrorになります。

コンポーネント図

命令形toolと宣言形formは、最終的に同じtool mapへ登録されます。
ページ内エージェントはRegisteredToolを取得して実行し、ブラウザ内蔵エージェントは観察経路からtool mapを取得します。

registerTool toolname 注釈 getTools executeTool in parallel 許可判定 tool map 収集 観察要求 ページスクリプト form 要素 iframe 内ページエージェント ModelContext tool map local pending map imperative execute steps declarative execute steps webmcp task source pending tool executions map セキュリティゲート 観察経路 Chrome 内蔵エージェント

クロスオリジンiframeでは三つの条件が関係します。
親側のallow="tools"、登録側のexposedTo、発見側のfromOriginsです。
Permissions Policyは子DocumentがAPIを使えるかを制御し、残り二つはどのオリジンへtoolを見せるかを制御します。

なお、CloudflareのHTMLRewriter注入やMCP-B polyfillはこの内部構造の一部ではありません。
ネイティブAPIの手前、または互換レイヤとして動作します。

データ

WebMCPの公開IDLと内部structは、登録入力、内部のtool definition、発見時のRegisteredToolを分離しています。
executeコールバックは登録側にだけ存在し、発見側へは渡りません。

概念モデル

DocumentがModelContextを所有し、ModelContextがtool定義とローカルの実行状態を所有します。
TraversableはDocumentをまたぐ実行状態を別に保持します。

Document ModelContext ModelContextStruct ToolDefinition TraversableNavigable HTMLFormElement Observation ModelContextTool ModelContextRegisterToolOptions RegisteredTool ToolAnnotations SubmitEvent PermissionsPolicyFeature LocalPendingToolExecution Annotations PendingToolExecution FormAssociatedElement
データ 役割
ModelContextTool registerToolへ渡す登録入力
ToolDefinition tool map内の内部的な正本
RegisteredTool ページ内エージェントへ返す実行用descriptor
LocalPendingToolExecution 対象Document内のAbortControllerを含む実行状態
PendingToolExecution callerとtargetをまたぐTraversable側の実行状態
Observation ブラウザエージェント向けの実装定義スナップショット

情報モデル

公開dictionaryと内部structの関係を、主要フィールドに絞って型レベルで示します。
Draft仕様では、inputSchemaは登録時にはJavaScript objectですが、内部ではJSON文字列として保持され、RegisteredToolでは再びobjectへ投影されます。

Document Origin: origin boolean: fullyActive ModelContext EventHandler: ontoolchange ModelContextStruct map: toolMap map: localPending ToolDefinition DOMString: name DOMString: inputSchema algorithm: executeSteps Annotations boolean: readOnlyHint boolean: untrustedContentHint TraversableNavigable map: pendingExecutions PendingToolExecution DOMString: toolName Document: callerDocument Document: targetDocument ModelContextTool DOMString: name USVString: title DOMString: description object: inputSchema ToolExecuteCallback: execute RegisteredTool DOMString: name DOMString: title DOMString: description object: inputSchema USVString: origin HTMLFormElement DOMString: toolname DOMString: tooldescription boolean: toolautosubmit PermissionsPolicyFeature DOMString: name DOMString: defaultAllowlist associated 1 1 internalContext 1 1 toolMap 1 many annotations 1 0..1 pendingMap 1 many registersAs many 1 projectsTo 1 many synthesizes 1 1 gates 1 many

重要な公開制約は次のとおりです。

項目 制約
name 1〜128文字、ASCII英数字と_ - .のみ
description 必須かつ空文字不可
inputSchema JSON直列化可能なobject
exposedToとfromOrigins potentially trustworthyなoriginのみ
RegisteredTool windowとoriginを含み、executeは含まない
annotations readOnlyHintとuntrustedContentHint、既定はfalse

Draft仕様とChrome OT実装では、公開される型が一致していません。
実装対象に合わせて明示的に変換してください。

API Draft仕様 Chrome 149〜156 OT実装
registerToolの戻り値 Promise<undefined> Chrome 149初期実装はundefined。後続版は実ブラウザで要確認
RegisteredTool.inputSchema object JSON文字列。利用時にJSON.parse()
executeToolの入力 object valid JSON string。JSON.stringify()して渡す
executeToolの結果 DOMString serialized string、ナビゲーション時はnull

Draft仕様準拠の将来実装へJSON文字列を渡すと、内部のstringifyで二重エンコードされる可能性があります。
一方、現行Chrome OTで動かすコードはJSON文字列を基準にします。
Chrome 149初期実装ではawait registerTool()と書いてもawait undefinedになるだけで、登録完了を待つ保証にはなりません。
マイルストーン更新時は戻り値の実形も確認してください。

構築方法

WebMCPは実験的APIであり、まず有効化経路を決める必要があります。
開発端末ではChrome flag、本番検証ではOrigin Trial、未対応ブラウザでは制約付きpolyfillを使えます。

前提条件

  • HTTPS、またはhttp://localhost
  • origin-keyed agent cluster
  • Permissions Policy toolsの許可
  • 対応ブラウザ、Origin Trial、またはpolyfill

Origin-Agent-Cluster: ?0やdocument.domainを使うと、WebMCP APIはSecurityErrorになります。
明示的に固定する場合は次のヘッダーを使用できます。

Origin-Agent-Cluster: ?1
Permissions-Policy: tools=(self)

Chromeでローカル開発する

  1. chrome://flags/#enable-webmcp-testingを開く
  2. WebMCP for testingをEnabledにする
  3. DevToolsのWebMCPペインも使う場合はchrome://flags/#devtools-webmcp-supportも有効にする
  4. Chromeを再起動する
  5. DevToolsコンソールでAPIを確認する
console.log("modelContext:", !!document.modelContext);
console.log("getTools:", typeof document.modelContext?.getTools);
console.log("executeTool:", typeof document.modelContext?.executeTool);

旧実装にはnavigator.modelContextTestingがありましたが、Chromium mainから削除されています。
testing flagを使う場合も、現行コードはdocument.modelContextを呼び出します。

Origin Trialを導入する

Chrome 149〜156ではOrigin Trial登録ページからオリジンを登録し、トークンをHTTPヘッダーまたはmeta要素で配信します。

Origin-Trial: TOKEN_GOES_HERE
<meta http-equiv="origin-trial" content="TOKEN_GOES_HERE">

iframeは親のトークンを継承しません。
APIを呼ぶJavaScriptのオリジンに一致するトークンが必要です。
ChromeとEdgeのOrigin Trialは別レジストリなので、トークンを共用できません。

Chrome DevToolsで確認するOrigin Trialの状態

型定義と互換レイヤを追加する

TypeScript型だけが必要な場合はwebmcp-typesを追加します。

npm install --save-dev webmcp-types
{
  "compilerOptions": {
    "types": ["webmcp-types"]
  }
}

MCP-B polyfillは未対応ブラウザ向けの仕様外レイヤです。

npm install @mcp-b/webmcp-polyfill
import { initializeWebMCPPolyfill } from "@mcp-b/webmcp-polyfill";
initializeWebMCPPolyfill();

polyfillでは、クロスDocumentのexposedToとfromOrigins、一部の宣言形UI状態、ナビゲーションをまたぐ応答を利用できません。
ネイティブ実装との機能差を前提にしてください。

利用方法

最小構成はtoolの登録、発見、実行です。
登録寿命と実行キャンセルにはそれぞれAbortSignalを使います。

命令形toolを登録する

const registration = new AbortController();

await document.modelContext.registerTool({
  name: "find_product",
  description: "商品名またはSKUで商品を検索する。価格と在庫を返す。",
  inputSchema: {
    type: "object",
    properties: {
      query: { type: "string", description: "商品名またはSKU" },
    },
    required: ["query"],
  },
  annotations: {
    readOnlyHint: true,
    untrustedContentHint: false,
  },
  execute: async ({ query }, options) => {
    if (typeof query !== "string" || query.trim() === "") {
      return { error: "query must be a non-empty string" };
    }
    // Draftでは第2引数にsignalが入る。Chrome 149初期OTではoptions自体が来ない。
    const requestOptions = options?.signal ? { signal: options.signal } : {};
    const response = await fetch(
      `/api/products?q=${encodeURIComponent(query)}`,
      requestOptions,
    );
    return await response.json();
  },
}, { signal: registration.signal });

// 登録解除
registration.abort();

専用のunregisterTool()はありません。
登録時に渡したsignalをabortするとtool mapから削除され、toolchangeが通知されます。

toolを発見して実行する

const tools = await document.modelContext.getTools();
const tool = tools.find((item) => item.name === "find_product");

if (!tool) {
  throw new Error("find_product is not registered");
}

const execution = new AbortController();
// Chrome OTではinputSchemaとexecuteToolの引数がJSON文字列です。
const inputSchema = JSON.parse(tool.inputSchema);
console.log("schema:", inputSchema);

const rawResult = await document.modelContext.executeTool(
  tool,
  JSON.stringify({ query: "ABC-123" }),
  { signal: execution.signal },
);

// find_productはobjectを返す契約なのでJSONとしてdecodeする。
const result = rawResult === null ? null : JSON.parse(rawResult);
console.log(result);

上の例はChrome 149〜156のOT実装向けです。
Draft仕様どおりの実装では、inputSchemaとexecuteToolの第2引数はobjectであり、変換は不要です。
toolがナビゲーションを起こす場合はnullを分岐してから結果をparseします。
文字列を返すtoolはAdded to-do: Buy milkのような非JSON結果になり得るため、汎用コードで無条件にJSON.parse()してはいけません。
toolごとの出力契約に従い、通常文字列はrawのまま扱います。

Chrome 149初期OTは、登録したexecuteコールバックへ入力objectだけを渡します。
上の登録例のように第2引数をoptionalとして扱えばDraft準拠実装とも共存できますが、未対応版では実処理へのAbortSignal伝播は働きません。

古いgetTools()結果を長く保持しないでください。
Documentのナビゲーションやtoolの再登録後は、descriptorを取得し直します。

一覧の変化はtoolchangeで購読できます。

document.modelContext.addEventListener("toolchange", async () => {
  const tools = await document.modelContext.getTools();
  updateAgentToolRegistry(tools);
});

クロスオリジンiframeへ公開する

親はPermissions Policyで機能を委譲します。

<iframe src="https://partner.example" allow="tools"></iframe>

登録側は公開先を指定します。

await document.modelContext.registerTool({
  name: "lookup_office",
  description: "現在選択中の拠点情報を返す。",
  execute: () => ({ office: "Building 4" }),
}, {
  exposedTo: ["https://caller.example"],
});

呼び出し側は検索対象を明示します。

const tools = await document.modelContext.getTools({
  fromOrigins: ["https://partner.example"],
});

allow="tools"、exposedTo、fromOriginsのいずれかが欠けると期待したtoolは見えません。

宣言形フォームをtoolにする

フォームにtoolnameとtooldescriptionを付けると、Chromeはフォームコントロールから入力schemaを合成します。

<form toolautosubmit
  toolname="support_request"
  tooldescription="問い合わせを担当チームへ送信する。"
  action="/support">
  <label for="firstName">First Name</label>
  <input id="firstName" type="text" name="firstName">

  <select name="team" required
    toolparamdescription="問い合わせを転送する担当チーム">
    <option value="Customer happiness team">返品</option>
    <option value="Distribution team">配送状況</option>
    <option value="Website support team">Webサイトの問題</option>
  </select>

  <button type="submit">送信</button>
</form>

エージェント起点のsubmitはSubmitEvent.agentInvokedで識別します。

document.querySelector("form").addEventListener("submit", (event) => {
  if (!event.agentInvoked) return;
  event.preventDefault();
  event.respondWith(Promise.resolve("Support request submitted"));
});

宣言形APIは仕様本文に未確定部分があります。
フォームコントロールごとのschema合成やキャンセルイベント名は、実装変更を見込んでChromeの現行ドキュメントと実ブラウザで確認してください。

DevTools ApplicationパネルのWebMCP

登録済みtoolの一覧

DevToolsからの手動実行

運用

実運用では、ブラウザごとにnative、Origin Trial、polyfillの経路が分かれます。
機能検出、トークン期限、実行ログ、動的登録の寿命をまとめて管理します。

機能検出と有効化経路

function detectWebMCP() {
  return {
    native: "modelContext" in Document.prototype,
    available: Boolean(document.modelContext),
  };
}

navigator.modelContextTestingは削除済みの旧診断APIなので、現行の機能検出には含めません。

環境 推奨経路 注意点
ローカル #enable-webmcp-testing testing APIを本番へ持ち込まない
ステージング Origin Trial staging origin専用トークン
Chrome 149〜156本番 Origin Trial 6週間の期限とtrial上限
未対応ブラウザ polyfill cross-document機能なし
Cloudflareゾーン bridge注入 native WebMCPがなければno-op

Origin Trialの状態はDevToolsのApplication、Frames、Origin Trialsで確認できます。
Success以外では、期限、origin不一致、trial無効化を切り分けます。

状態確認とログ

const tools = await document.modelContext.getTools();
console.table(tools.map((tool) => ({
  name: tool.name,
  origin: tool.origin,
  readOnly: tool.annotations?.readOnlyHint,
  untrusted: tool.annotations?.untrustedContentHint,
})));

Chrome DevToolsのApplicationパネルにあるWebMCPペインでは、利用可能なtool、呼び出し回数、入力、出力、Completed、Canceled、In Progress、Errorを確認できます。
Chrome DevTools ProtocolにもWebMCP.enable、invokeTool、cancelInvocation、toolsAdded、toolInvoked、toolRespondedがあります。

toolのdescription、入力、出力はすべて非信頼データとしてログへ保存してください。
特にtoolResponded.outputを、そのまま次のモデル入力へ連結しないことが重要です。

動的登録とページ破棄

状態依存toolだけを動的登録し、静的に使えるtoolはページ初期化時に一度登録する方が安定します。

let checkoutController;

function syncCheckoutTool(cart) {
  if (cart.items.length === 0) {
    checkoutController?.abort();
    checkoutController = undefined;
    return;
  }
  if (checkoutController) return;

  checkoutController = new AbortController();
  document.modelContext.registerTool({
    name: "start_checkout",
    description: "現在のカートで購入確認画面を開く。注文は確定しない。",
    inputSchema: { type: "object", properties: {} },
    execute: () => {
      location.assign("/checkout");
      return "Navigated to checkout";
    },
  }, { signal: checkoutController.signal });
}

Draft仕様では、registerToolとgetToolsの対象Document、またはexecuteToolの呼び出し元Documentがfully activeでない場合にInvalidStateErrorになります。
一方、toolを所有する対象DocumentがBFCache中なら、Draft上は復帰まで実行タスクを待ちます。
caller Documentが破棄されると実行はcancelされ、target Documentが破棄されると呼び出しPromiseは失敗します。
Chrome OTの挙動はマイルストーンごとに実ブラウザで確認してください。

ページ単位の停止

サイト全体で即時停止したいときは、レスポンスヘッダーを次に変更します。

Permissions-Policy: tools=()

この設定はgetterを消すのではなく、APIメソッドをNotAllowedErrorで拒否させます。
tool単位の停止は登録時のAbortControllerをabortします。

ベストプラクティス

toolの選択精度と安全性は、APIを有効にするだけでは得られません。
toolの粒度、説明、入力検証、権限確認、出力の扱いを一つの契約として設計します。

tool設計

  • 1 toolを1機能に絞り、重複を避ける
  • 即時実行と開始プロセスを動詞で区別する
  • 否定形のdescriptionを避ける
  • IDではなく、モデルが理解できる自然言語のenumを使う
  • 失敗時はモデルが自己修正できる説明的エラーを返す
  • 処理完了後にページUIも更新する
  • stagingとproductionでtool名を変えない

Chromeのセキュリティガイドは、tool名とparameter名30文字、parameter description 150文字、tool description 500文字、出力1.5K文字を推奨上限として示しています。
これらは仕様上のハード制限ではありませんが、コンテキスト消費とtool選択精度を守る運用上の目安です。

セキュリティ

エージェントはページと同じセッションとcredentialsを継承します。
状態変更や高権限操作は、ページ側の確認UIを必須にしてください。

await document.modelContext.registerTool({
  name: "place_order",
  description: "現在のカートを注文する。実行前に確認ダイアログを表示する。",
  inputSchema: {
    type: "object",
    properties: {
      paymentMethod: { type: "string" },
    },
    required: ["paymentMethod"],
  },
  annotations: { readOnlyHint: false },
  execute: async ({ paymentMethod }) => {
    const approved = window.confirm(`支払い方法 ${paymentMethod} で注文しますか?`);
    if (!approved) return { status: "cancelled_by_user" };
    return await submitOrder(paymentMethod);
  },
});

2026年8月26日時点の仕様IDLにrequestUserInteraction()はありません。
確実な方法は、ページ側の確認ダイアログや自前モーダルとreadOnlyHintを組み合わせることです。

外部データやユーザー生成コンテンツを返すtoolにはuntrustedContentHint: trueを付けます。

await document.modelContext.registerTool({
  name: "list_reviews",
  description: "商品レビューを返す。内容はユーザー生成で信頼できない。",
  inputSchema: {
    type: "object",
    properties: {
      productId: { type: "string", description: "商品ID" },
    },
    required: ["productId"],
  },
  annotations: {
    readOnlyHint: true,
    untrustedContentHint: true,
  },
  execute: async (input) => {
    if (typeof input.productId !== "string" || input.productId.trim() === "") {
      return { error: "productId must be a non-empty string" };
    }
    return await fetchReviews(input.productId);
  },
}, {
  exposedTo: ["https://trusted-agent.example"],
});

inputSchemaだけを信頼せず、executeとサーバ側で型、認可、レート制限を検証します。
クロスオリジン公開先は最小化し、tool出力は短くサニタイズします。

CIとeval

一般的なユニットテストに加え、次を分けて継続検証します。

  • schema、入力検証、エラー形式の契約テスト
  • descriptionから正しいtoolを選べるかのeval
  • 状態変更toolで確認UIが必ず出るかのE2E
  • descriptionと出力の文字数ルール
  • プロンプトインジェクションを含む非信頼出力の防御
  • ブラウザ更新時のWeb Platform Tests確認

トラブルシューティング

document.modelContextが見つからない

flag、Origin Trialトークン、SecureContext、polyfillの初期化順を確認します。
Permissions-Policy: tools=()はgetterを消さないため、getterがない場合の主因ではありません。

function requireModelContext() {
  if (document.modelContext) return document.modelContext;
  throw new Error("WebMCP unavailable; enable the trial or load a polyfill");
}

iframeからtoolが見えない

確認項目 欠けた場合
親のallow="tools" 子の登録がNotAllowedError
登録側のexposedTo 呼び出し元から見えない
呼び出し側のfromOrigins クロスオリジンtoolが一覧に出ない
同一Traversable 別タブならUnknownError
descriptorのorigin一致 ナビゲーション後はUnknownError

DOMExceptionから原因を特定する

例外 主な原因 対処
InvalidStateError fully activeでない、同名登録、name規則違反 復帰後に再試行、登録解除、name修正
SecurityError origin-keyedでない、信頼できないorigin指定 origin isolationとHTTPSを確認
NotAllowedError Permissions Policy toolsが不許可 親のallowとレスポンスヘッダーを確認
UnknownError 別Traversable、ナビゲーション後のorigin不一致 同一タブでdescriptorを再取得
NotSupportedError opaque origin、polyfill非対応機能 localhostまたはnative実装を使用
TypeError inputSchemaをJSON化できない プレーンobjectへ修正

同名toolのschema更新で競合する

登録解除と実行が競合すると、古いRegisteredToolに対して新しいschemaのtoolが実行される可能性があります。
破壊的なschema変更ではtool名を変更するか、abort後のtoolchangeを待って再登録します。
実行側でも必ず入力を検証し、説明的なエラーを返します。

Cloudflare注入後もtoolが見えない

bridgeはブラウザにnative WebMCPがない場合no-opです。
/.webmcp/bridge.jsとMCPエンドポイントの配信状態も確認します。

curl -sI https://your-site.example/mcp
curl -s https://your-site.example/.webmcp/bridge.js | head

まとめ

WebMCPは、Webページの機能を型付きtoolとしてブラウザエージェントへ公開し、人間向けUIとエージェント操作を同じDocument上で共存させるAPIです。
実装の中心はdocument.modelContext、registerTool、getTools、executeToolと、HTMLフォームを利用する宣言形APIです。

導入時は、まだDraftとOrigin Trial段階であることを前提に、APIの現行差分、Origin Trialの期限、Permissions Policy、origin isolation、polyfillの制約を管理する必要があります。
さらに、ページと同じ権限を使う設計だからこそ、入力の再検証、状態変更前の確認、非信頼出力の隔離、クロスオリジン公開の最小化が欠かせません。

この記事が少しでも参考になった、あるいは改善点などがあれば、ぜひリアクションやコメント、SNSでのシェアをいただけると励みになります!

参考リンク