予約や見積もりの操作を、AIエージェントがページから直接呼び出せるようにするWebMCP(ページがツールを登録する、実験中のブラウザーのAPI)に対応すると、確認する項目が1つ増えます。画面の表示やボタンの動作に加えて、「エージェント向けのツールが登録されているか」「正しい結果を返すか」です。ツールは画面に表示されないため、目視の確認では見落とします。
2026年10月7日(UTC)公開のPlaywright 1.64で、ページが登録したツールの一覧を取る page.webmcp.tools() と、ツールを呼び出す page.webmcp.callTool() が加わりました。リリースノートには「so you can test them like any other part of your app」(アプリのほかの部分と同じようにテストできる)とあります。
本記事では、PlaywrightのリポジトリとWebMCPの仕様の原稿を確認したうえで、編集部の検証環境で @playwright/test 1.64.1 を使い、検証用のページに登録したツールを実際にテストしました。Playwright 1.64が対象とするChromiumの版は 156.0.8078.4 で、Playwrightの設定ファイル(browsers.json)でもこのブラウザーは「Chrome for Testing」と書かれています。Playwrightの配布元からはこの環境で取得できなかったため、Googleが配布する同じ版番号のChrome for Testingを使いました。
有効にしないと動かない
WebMCPは実験中の機能で、Chromiumでは起動時の引数 --enable-features=WebMCP で有効にします(Firefoxは設定項目、WebKitは未対応とPlaywrightの文書にあります)。引数を付けずに tools() を呼ぶと、次のエラーになりました。
webMCP.tools: WebMCP is not enabled. Launch the browser with the "--enable-features=WebMCP" argument.
テストの設定では、launchOptions.args に引数を入れておきます。
登録の入口は document.modelContext だった
検証で最初につまずいたのが、ツールを登録する入口です。PlaywrightのAPIの文書には、WebMCPのAPIを navigator.modelContext と書いています。ところが、Chrome for Testing 156で引数を付けて開いたページでは、navigator.modelContext は undefined で、document.modelContext だけがありました。navigator.modelContext で登録するように書いたページでは、tools() が空の配列を返し、callTool() は「No WebMCP tool named “estimate_price”.」で失敗しました。
WebMCPの仕様の原稿(webmachinelearning/webmcp)でも、APIは Document の modelContext として定義されており、安全なコンテキスト(HTTPSか、localhostのような扱いのページ)に限られます。Playwright 1.64の実装は、document.modelContext を先に探し、なければ navigator.modelContext を使う作りでした。
以前の解説や実装例で navigator.modelContext を使っている場合は、次のように両方を見る書き方にしておくと、どちらの環境でも登録されます。WebMCPの考え方はWebMCPとは、実装の進め方はWebMCPの実装の記事で整理しています(0445の記事は navigator.modelContext で書いていたため、2026年10月19日に document.modelContext へ訂正しました)。
<script>
// 仕様の原稿は document.modelContext。古い解説の navigator.modelContext も見ておく
const mc = document.modelContext ?? navigator.modelContext;
mc?.registerTool({
name: 'estimate_price',
description: 'ページ数から概算を返す(検証用の架空の計算)',
inputSchema: { type: 'object', properties: { pages: { type: 'number' } }, required: ['pages'] },
annotations: { readOnlyHint: true }, // 仕様の原稿のキー名。tools() の結果では readOnly になる
async execute({ pages }) {
if (!(pages > 0)) return { content: [{ type: 'text', text: 'pages must be positive' }], isError: true };
return { content: [{ type: 'text', text: String(pages * 2) }] };
},
});
</script>

テストのコード
検証用のページに対して実行したテストです。Chrome for Testing 156では「1 passed」になりました。
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
use: {
baseURL: 'http://127.0.0.1:8790',
launchOptions: {
args: ['--enable-features=WebMCP'],
executablePath: process.env.CHROME_PATH, // 検証環境では Chrome for Testing 156 を指定
},
},
});
// tests/webmcp.spec.ts
import { test, expect } from '@playwright/test';
test('見積もりツールが登録され、正しく計算する', async ({ page }) => {
await page.goto('/page2.html');
// ツールが0件でもループは素通りするため、件数と名前を先に確かめる
const tools = await page.webmcp.tools();
expect(tools.map(t => t.name)).toContain('estimate_price');
const ok = await page.webmcp.callTool('estimate_price', { pages: 21 });
expect(ok).toEqual({ content: [{ type: 'text', text: '42' }] });
// 不正な入力はツール側で isError を返す設計にしておく
const ng = await page.webmcp.callTool('estimate_price', { pages: 0 });
expect(ng).toMatchObject({ isError: true });
});
executablePath は、Playwrightが配布するブラウザーを使える環境なら不要です。
動かして分かった3つの落とし穴
1つ目は、古いブラウザーでは失敗せずに空になることです。 この環境にもともとあったChromium 141(Playwright 1.64が対象とする版より古いもの)に同じ引数を付けて実行すると、tools() はエラーにならず空の配列を返しました。文書には「Throws if the browser was launched without WebMCP support」とありますが、今回の組み合わせでは例外になりませんでした。「登録されたツールをすべて呼んで確かめる」ようなループだけのテストだと、0件のまま成功してしまいます。Playwright 1.64.1の実装を読むと、有効になっているかの判定は起動時の引数を見ているだけで、ブラウザーがWebMCPに対応しているかまでは見ていません。上のテストのように、名前で存在を確かめる一文を必ず入れます。実際にChromium 141で実行すると、この一文で失敗しました(Received array: [])。
2つ目は、入力の型をブラウザーが検査しないことです。 pages を数値と定めたツールに文字列の 'abc' を渡しても、callTool() は例外にならず、ツールの execute まで届きました。Playwrightの文書も、ツールの名前・説明・入力の定義・結果は「treat them as untrusted input」(信頼できない入力として扱う)としています。入力の検証はツールの中で行い、不正な値には isError: true を返す設計にして、その分岐もテストします。isError の結果は、文書のとおりそのまま返ってきました。
3つ目は、annotations のキー名です。 検証では登録時に annotations: { readOnly: true } と書き、tools() の結果には annotations が含まれませんでした。後から仕様の原稿を確かめると、登録時のキーは readOnlyHint・untrustedContentHint・consequentialHint で、readOnly は仕様にない名前でした。一方、Playwrightの文書に載っている返り値の項目は readOnly・untrustedContent・consequential です。Playwright 1.64.1の実装は、ブラウザーから受け取った readOnlyHint などをこの名前に読み替え、どれも true でなければ annotations 自体を返しません。返らなかったのは、検証用のページのキー名の誤りが原因だった可能性が高いと編集部は考えています。登録するページでは readOnlyHint、テストで見る結果では readOnly、と名前が違う点に注意してください。上のHTMLの例は readOnlyHint に直していますが、直した後のページでは実行していません。
納品前の確認に組み込むなら(編集部の提案)
- ブラウザーの版を固定する: 実験中の機能は版によって入口や挙動が変わります。テストを実行するブラウザーの版を記録し、版を上げるときにWebMCPのテストも見直します
- ツールの一覧を仕様書と突き合わせる: 発注時に決めたツールの名前と入力の定義を、
tools()の結果と比べるテストを1本用意すると、追加・削除の漏れに気づけます - 呼び出しは検証用のデータで行う:
callTool()は本当にツールの処理を実行します。予約や注文のような取り消しにくい操作を持つツールは、テスト用の環境・アカウントで呼び出します
まずは、WebMCPに対応したページ(または対応予定のページ)で、ツールが1つ登録されることを確かめるテストを1本書き、--enable-features=WebMCP の有無で結果が変わることを手元で見ておくと、設定漏れを早い段階で見つけられます。
2026年10月19日に、microsoft/playwright の v1.64.1 タグの
docs/src/api/class-webmcp.md・class-page.md・packages/playwright-core/browsers.jsonとリリースノートの原稿、playwright.dev のリリースノートとWebMCPのAPIの文書、GitHubのリリース、npmの公開日時、playwright-core 1.64.1 の実装、webmachinelearning/webmcp のindex.bs・README.mdを確認しました(資料調査)。編集部の検証環境(Linux、Node.js v22.22.0、@playwright/test 1.64.1)で、Chrome for Testing 156.0.8078.4とChromium 141.0.7390.37を使い、検証用のページに登録したツールの一覧取得・呼び出しと本文のテストを実行しました(実機検証)。本文のHTMLの例は、照合の際にannotationsのキー名だけをreadOnlyHintに直しており、直した後は実行していません。Playwrightが配布するChromiumそのもの、Firefox、通常のChromeの安定版、Playwright MCPや実際のAIエージェントからの呼び出しは確認していません。
WebMCPに対応したサイトや業務システムの開発、納品前のテストの仕組みづくりは、グリームハブの開発・AI・自動化のご相談で承っています。対応する操作や公開範囲によって進め方が変わるため、お問い合わせからご相談ください。
参考資料
- Playwright release notes(microsoft/playwright, docs/src/release-notes-js.md)
- class: WebMCP(microsoft/playwright v1.64.1, docs/src/api/class-webmcp.md)
- microsoft/playwright v1.64.1 — packages/playwright-core/browsers.json
- webmachinelearning/webmcp — index.bs
- webmachinelearning/webmcp — README.md
- npm registry — @playwright/test









