Nexta Tech Blog
🔍

Chrome DevTools MCP vs Playwright MCP - どちらを選ぶべき?実測で比較

に公開
4

訂正とお詫び(2025/11/09追記)

記事公開後、Playwright MCPコントリビューターのogadra様より、技術的な誤りをご指摘いただきました。深くお詫び申し上げます。

誤った記述

「Playwright MCPはAIがセレクターを自動生成する」

正しい動作

Playwright MCPもChrome DevTools MCPと同様に、スナップショットに含まれる識別子(ref)を使用して要素を特定します。

  • LLMはセレクターを生成していません
  • mcp__playwright__browser_fill_formなどの関数呼び出し時、引数としてref(Chrome DevTools MCPのuidに相当)が渡されます
  • 両MCPの要素識別方法に本質的な違いはありません

実際の違い

主な違いは以下の2点です:

  1. 操作の実行方法

    • Chrome DevTools MCP: CDP(Chrome DevTools Protocol)で直接操作
    • Playwright MCP: 内部でPlaywrightライブラリを使って操作を実行後、同等の処理を行うPlaywrightコード例をレスポンスに含める(説明用)
  2. アクセシビリティツリーの情報量の差

    • 取得できる要素情報の詳細度が異なる

以下、修正済みの内容となります。貴重なご指摘をいただいたogadra様に改めて感謝申し上げます。


はじめに

Claude Codeでブラウザテストするとき、Chrome DevTools MCPとPlaywright MCPのどちらを使うべきか迷っていませんか?

この記事では、同じBlazorアプリで両方を実際に使い、選び方の基準を示します。

結論を先に:選び方の基準

用途 おすすめ 理由
デバッグ・要素特定 Chrome DevTools MCP UIDで要素を確実に指定
パフォーマンス分析 Chrome DevTools MCP Core Web Vitals測定可能
標準的なフォーム操作 Playwright MCP Playwrightコード形式で操作
探索的テスト Playwright MCP 操作手順がシンプル
CI/CD自動テスト どちらも不向き 従来のPlaywright/Selenium推奨

検証環境

Blazor ServerとRadzen Componentsで作成したTest Formページを使用:

  • URL: https://localhost:7286/test-form
  • フィールド: Name、Email、Age、Country(ドロップダウン)
  • 動作: Submit後に成功メッセージ表示

実際の検証例:Playwright MCPでBlazorフォーム操作

上記のBlazor Radzenアプリで、実際にPlaywright MCPを使ってフォーム操作を検証しました。

検証シナリオ

Name、Email、Age、Countryフィールドにデータを入力してSubmitするまでの一連の操作を実行。

実際に実行したMCPツールとレスポンス

1. ページに移動

MCPツール呼び出し:

mcp__playwright__browser_navigate({ url: "http://localhost:5158/test-form" })

レスポンス(Ran Playwright code):

await page.goto('http://localhost:5158/test-form');

2. フォーム入力(Name、Email、Age)

MCPツール呼び出し:

mcp__playwright__browser_fill_form({
  fields: [
    { name: "Name", type: "textbox", ref: "e104", value: "John Doe" },
    { name: "Email", type: "textbox", ref: "e107", value: "john@example.com" },
    { name: "Age", type: "textbox", ref: "e111", value: "30" }
  ]
})

レスポンス(Ran Playwright code):

await page.locator('#Name').fill('John Doe');
await page.locator('#Email').fill('john@example.com');
await page.locator('#Age').fill('30');

3. Countryドロップダウンを開く

MCPツール呼び出し:

mcp__playwright__browser_click({ element: "Country dropdown", ref: "e114" })

レスポンス(Ran Playwright code):

await page.getByText('Japan USAJapanUKGermanyFranceCanada').click();

4. USAを選択

MCPツール呼び出し:

mcp__playwright__browser_click({ element: "USA option", ref: "e135" })

レスポンス(Ran Playwright code):

await page.getByRole('option', { name: 'USA' }).click();

5. Submitボタンをクリック

MCPツール呼び出し:

mcp__playwright__browser_click({ element: "Submit button", ref: "e126" })

レスポンス(Ran Playwright code):

await page.getByRole('button', { name: 'Submit' }).click();

結果:

Success! Form submitted successfully! Name: John Doe, Email: john@example.com

検証から得られた知見

  1. MCPツール使用後、すぐにPlaywrightコードが確認できる

    • await page.locator('#Name').fill('John Doe') など、そのまま使えるコード
    • テストコード作成の参考になる
  2. getByRole()など、Playwrightのベストプラクティスが使われる

    • アクセシビリティを意識したセレクター
    • メンテナンスしやすいテストコードの書き方を学べる
  3. MCPで動作確認→コードをテストスイートに追加、という流れが効率的

    • 手動検証とテスト自動化を同時に進められる
    • レスポンスのコードをコピペするだけ

実際の検証例:Chrome DevTools MCPでBlazorフォーム操作

同じBlazor Radzenアプリで、Chrome DevTools MCPを使ってフォーム操作を検証しました。

検証シナリオ

Playwright MCPと同じく、Name、Email、Age、Countryフィールドにデータを入力してSubmitするまでの一連の操作を実行。

実際に実行したMCPツールとレスポンス

1. ページに移動

MCPツール呼び出し:

mcp__chrome-devtools__navigate_page({ 
  type: "url", 
  url: "http://localhost:5158/test-form" 
})

レスポンス:

Successfully navigated to http://localhost:5158/test-form.
## Pages
0: http://localhost:5158/test-form [selected]

2. スナップショット取得(UID確認)

MCPツール呼び出し:

mcp__chrome-devtools__take_snapshot()

レスポンス(一部抜粋):

uid=1_22 textbox "  "
uid=1_23 StaticText "Name"
uid=1_24 textbox "  "
uid=1_25 StaticText "Email"
uid=1_26 textbox "  " value="0"
uid=1_27 StaticText "Age"
uid=1_28 generic
  uid=1_29 textbox "Japan" haspopup="listbox" readonly value="Japan"
uid=1_32 StaticText "Country"
uid=1_38 button "SUBMIT"

3. フォーム入力(Name、Email、Age)

MCPツール呼び出し:

mcp__chrome-devtools__fill_form({
  elements: [
    { uid: "1_22", value: "John Doe" },
    { uid: "1_24", value: "john@example.com" },
    { uid: "1_26", value: "30" }
  ]
})

レスポンス:

Successfully filled out the form
## Page content
uid=2_22 textbox "  " value="John Doe"
uid=2_24 textbox "  " value="john@example.com"
uid=2_26 textbox "  " value="30"

4. Countryドロップダウンを開く

MCPツール呼び出し:

mcp__chrome-devtools__click({ 
  element: "Country dropdown container", 
  uid: "3_30" 
})

レスポンス:

Successfully clicked on the element
## Page content
uid=4_28 generic "Japan Japan arrow_drop_down" focusable focused
uid=4_40 listbox orientation="vertical"

5. 詳細スナップショットでUSAのUIDを確認

MCPツール呼び出し:

mcp__chrome-devtools__take_snapshot({ verbose: true })

レスポンス(一部抜粋):

uid=6_134 listbox orientation="vertical"
  uid=6_135 option "USA" value="USA"
  uid=6_138 option "Japan" value="Japan"
  uid=6_141 option "UK" value="UK"
  ...

6. USAを選択

MCPツール呼び出し:

mcp__chrome-devtools__click({ 
  element: "USA option", 
  uid: "6_135" 
})

レスポンス:

Successfully clicked on the element
## Page content
uid=7_28 generic "USA USA arrow_drop_down" focusable focused
uid=7_29 textbox "USA" haspopup="listbox" readonly value="USA"

7. Submitボタンをクリック

MCPツール呼び出し:

mcp__chrome-devtools__click({ 
  element: "SUBMIT button", 
  uid: "7_38" 
})

レスポンス:

Successfully clicked on the element
## Page content
uid=8_21 StaticText "check_circle"
uid=8_22 StaticText "Success!"
uid=8_24 StaticText "Form submitted successfully! Name: John Doe, Email: john@example.com"

検証から得られた知見

  1. UID指定による確実な操作

    • スナップショットで要素のUIDを明示的に確認
    • 誤操作のリスクが低い
  2. ドロップダウン操作に複数ステップが必要

    • 開く → verbose スナップショット → 選択の3ステップ
    • Playwright MCPは2ステップで完了
  3. レスポンスは簡潔

    • コード例は含まれない
    • 操作結果とページ状態のみ表示

詳細比較:6つの観点

上記の検証を踏まえて、両MCPの違いを6つの観点から詳しく比較します。

1. 操作方法の違い

本質的な違いは「操作コードの形式」

Chrome DevTools MCP

  • take_snapshot でアクセシビリティツリーを取得し、UID(一意識別子)を付与
  • 操作時にはUIDで要素を明示的に指定
  • CDP(Chrome DevTools Protocol)で直接操作
  • 例: {"uid": "1_22", "value": "John Doe"}

Playwright MCP

  • アクセシビリティツリーから要素のref(識別子)を取得
  • 操作時にはrefを使ってPlaywrightライブラリで実際の操作を実行
  • 実行後、MCPのレスポンスに同等の処理を行うPlaywrightコード例が含まれる(説明用)
  • 例: 内部で操作実行後、await page.locator('#Name').fill('John Doe')という相当するコードをレスポンスに含める

2. フォーム入力

Chrome DevTools MCP

❌ 事前にスナップショットでUIDを確認する必要がある

mcp__chrome-devtools__take_snapshot()
→ 結果: uid=1_22 (Name), uid=1_24 (Email), uid=1_26 (Age)

mcp__chrome-devtools__fill_form([
  {"uid": "1_22", "value": "John Doe"},
  {"uid": "1_24", "value": "john@example.com"},
  {"uid": "1_26", "value": "30"}
])

Playwright MCP

✅ スナップショットから要素のrefを取得して入力

mcp__playwright__browser_fill_form({
  name: "Name input",
  type: "textbox",
  ref: "xxxxx",  // スナップショットで取得した識別子
  value: "John Doe"
})
→ 実行後、レスポンスに含まれるPlaywrightコード例(説明用):
  await page.locator('#Name').fill('John Doe');
  await page.locator('#Email').fill('john@example.com');
  await page.locator('#Age').fill('30');

3. ドロップダウン操作

Chrome DevTools MCP

❌ 3ステップ必要

mcp__chrome-devtools__click(uid="3_30")  // コンテナクリック
mcp__chrome-devtools__take_snapshot(verbose=true)  // オプション一覧を取得
mcp__chrome-devtools__click(uid="5_135")  // 目的のオプションをクリック

Playwright MCP

✅ 2ステップで完了(スナップショットから要素のrefを取得)

mcp__playwright__browser_click({
  ref: "xxxxx"  // ドロップダウンのref
})
mcp__playwright__browser_click({
  ref: "yyyyy"  // USAオプションのref
})
→ 実行後、レスポンスに含まれるPlaywrightコード例(説明用):
  // ドロップダウンを開く
  await page.getByText('Japan USAJapanUKGermanyFranceCanada').click();
  // USAオプションを選択
  await page.getByRole('option', { name: 'USA' }).click();

4. スナップショット(ページ構造の確認)

Chrome DevTools MCP

uid=1_0 RootWebArea "Test Form"
  uid=1_22 textbox "  "
  uid=1_23 StaticText "Name"
  uid=1_24 textbox "  "
  uid=1_25 StaticText "Email"
  uid=1_26 textbox "  " value="0"
  uid=1_27 StaticText "Age"

UIDが明確、デバッグしやすい

Playwright MCP

textbox "Name"
textbox "Email"
textbox "Age" [value=0]
combobox "Country"

人間が読みやすい、直感的

5. 使いやすさ

Chrome DevTools MCP

  • ✅ 細かい制御が可能
  • ✅ デバッグしやすい(UIDが明確)
  • ✅ 要素の特定が確実
  • ❌ AIの実行ステップが多い(操作の前に必ずスナップショット取得が必要)
  • ❌ UIDが動的に変わる(ページ更新や要素追加でUIDが変化する)

Playwright MCP

  • ✅ 操作手順がシンプル
  • ✅ Playwrightコード形式で実行内容が分かりやすい
  • ✅ 標準的なUI要素に強い
  • ❌ 実行コードの詳細はMCPレスポンスに依存
  • ❌ デバッグ時はPlaywrightコードを確認する必要がある

6. 共通機能と本質的な違い

両MCPの共通点

  • ✅ 内部的にアクセシビリティツリーを使用
  • ✅ 基本的なブラウザ操作(ドラッグ&ドロップ、キーボード操作、ホバー、ファイルアップロード)は同等に実行可能
操作 Chrome DevTools MCP Playwright MCP
ドラッグ&ドロップ drag browser_drag
キーボード操作 press_key browser_press_key
ホバー hover browser_hover
ファイルアップロード upload_file browser_file_upload

本質的な違いは「操作の実行方法とレスポンス形式」:

  • Chrome DevTools MCP: アクセシビリティツリーにUID(一意識別子)を付与し、CDP(Chrome DevTools Protocol)で直接操作
  • Playwright MCP: アクセシビリティツリーから要素のref(識別子)を取得し、Playwrightライブラリで操作を実行。実行後、同等の処理を行うPlaywrightコード例をレスポンスに含める

要素の識別方法に本質的な違いはありません。どちらもスナップショットに含まれる識別子を使用します。

どちらを選ぶかは、「CDP直接操作(Chrome DevTools MCP)」「Playwrightライブラリ実行+コード例提供(Playwright MCP)」の判断になります。

Claude Codeでのセットアップ

Chrome DevTools MCPのセットアップ

# MCPサーバーを追加
claude mcp add chrome-devtools npx chrome-devtools-mcp@latest

# 追加されたか確認
claude mcp

Playwright MCPのセットアップ

# MCPサーバーを追加
claude mcp add playwright npx @playwright/mcp@latest

# 追加されたか確認
claude mcp

Chrome DevTools MCP特有の機能:パフォーマンス分析

Chrome DevTools MCPの最大の特徴は、Playwright MCPにないパフォーマンス分析機能です。

Blazor Server(https://localhost:7286/test-form)で実際に検証した結果:

パフォーマンストレース結果

mcp__chrome-devtools__performance_start_trace({
  reload: true,
  autoStop: true
})

取得できたCore Web Vitals

LCP: 138 ms ✅ 良好(2.5秒以内)
  - TTFB: 25 ms ✅ 優秀(800ms以内)
  - Render delay: 114 ms(描画遅延)
CLS: 0.00 ✅ 完璧(0.1以下)

Chrome MCPが自動生成したパフォーマンス改善提案(6種類)

パフォーマンストレースを実行すると、Chrome DevTools MCPが測定結果を分析し、具体的な改善アクションを自動的に提案してくれます:

  1. LCPBreakdown: LCP最適化の提案
  2. CLSCulprits: レイアウトシフト防止策
  3. RenderBlocking: レンダリングブロック削減
  4. NetworkDependencyTree: ネットワーク依存関係最適化
  5. DocumentLatency: 初期ロード遅延削減(推定削減: 8.3 kB)
  6. Cache: キャッシュ戦略の提案

コンソールログ取得結果

mcp__chrome-devtools__list_console_messages()

取得できたBlazor特有のログ

[info] Normalizing '_blazor' to 'https://localhost:7286/_blazor'
[info] WebSocket connected to wss://localhost:7286/_blazor
[debug] CSS Hot Reload ignoring bootstrap.min.css (7000+ rules)
[debug] CSS Hot Reload ignoring material-base.css (7000+ rules)

→ Blazorの内部動作(WebSocket接続、CSS Hot Reload)を監視できます。

ネットワークリクエスト取得結果

mcp__chrome-devtools__list_network_requests({
  resourceTypes: ["document", "script", "xhr", "fetch"]
})

取得できたBlazor初期化リクエスト(10件)

GET /test-form [200]
GET /_framework/blazor.web.js [304]
GET /_content/Radzen.Blazor/Radzen.Blazor.js [200]
POST /_blazor/negotiate [200]

→ Blazorの初期化プロセス(SignalR negotiation、WebSocket接続)を詳細に確認できます。

実用例:パフォーマンス問題の検出

この検証で以下のことが分かりました:

  • LCPは良好(138ms < 2.5秒)
  • CLSは完璧(0.00 = レイアウトシフトなし)
  • ⚠️ Render delayがやや長い(114ms / 138ms = 82%)
    • 改善提案:レンダリングブロックリソースの削減

Chrome DevTools MCPを使えば、このような具体的な改善ポイントが自動で提示されます。

まとめ

Chrome DevTools MCPとPlaywright MCPは、どちらもアクセシビリティツリーを使い、要素の識別方法に本質的な違いはありません。主な違いは操作の実行方法とレスポンス形式です:

  • Chrome DevTools MCP: UID指定でCDP直接操作。パフォーマンス分析も可能
  • Playwright MCP: refを使ってPlaywrightライブラリで操作を実行。実行後、同等の処理を行うPlaywrightコード例をレスポンスに含める(説明用)

用途に応じて使い分けることで、効率的なブラウザテストが実現できます。

参考リンク

GitHubで編集を提案
Nexta Tech Blog
Nexta Tech Blog

Discussion

ogadraogadra

マサカリ失礼します。Playwright MCPのコントリビューターです。
記事には

アクセシビリティツリーからAIがセレクターを自動生成
操作時にはAIが適切なセレクター(name、role、text)を選択
例: await page.locator('#Name').fill('John Doe')

とありますが、実際にはLLMはセレクタを選択していることはなく、Chrome DevTools MCP同様にsnapshotに含まれた識別子を元に指示しています。
要素の識別方法においてChrome DevTools MCPとPlaywright MCPの違いはありません。

実行されるPlaywrightコードが示されるのは、LLMの指示内容に含まれるものではなく、Playwright MCP側のレスポンスに含まれる内容になります。

たとえば、記事にある「textboxを埋める」処理であればmcp__playwright__browser_fill_form関数が呼ばれます。その引数としては

  • name : ログとしての引数
  • type : 今回の場合はtextbox
  • ref : Chrome DevTools MCPのuidと同等
  • value : 今回の場合は Jone Dae

が渡されます。

https://github.com/microsoft/playwright/blob/daae9b5d7964432bed519ca3b462ca89e1012566/packages/playwright/src/mcp/browser/tools/form.ts#L29-L34

その後ツールが実行され、textboxをPlaywright MCPが埋めた後に、Playwright MCPが説明のため、Playwrightで実際に実行する場合どのようなコードを書けばよいか?ということをレスポンスに含めています。

https://github.com/microsoft/playwright/blob/daae9b5d7964432bed519ca3b462ca89e1012566/packages/playwright/src/mcp/browser/tools/form.ts#L43-L46

ブラウザ操作におけるPlaywright MCPとChrome DevTools MCPの違いは、この「Playwrightではどのようなコードを書けば同等の処理が実行できるか?」という内容がMCPレスポンスに含まれるかどうかの違いと、アクセシビリティツリーにおける情報量の差です。後者は記事内に言及がなかったので省略します。

tetsu.ktetsu.k

ご指摘ありがとうございます!
認識が甘く、大変失礼いたしました。

※記事を冒頭に訂正セクションを追加し、本文も全面的に修正しました。

Playwright MCPコントリビューターの方から直接ご指摘いただけて光栄です。
業務で使うために調べているところでしたので、正確な理解ができて本当に助かりました。

Playwright MCPの仕組みを理解でき、大変勉強になりました。
今後も活用させていただきます。

改めて、貴重なご指摘ありがとうございました!

ogadraogadra

修正ありがとうございます!
自分自身に知見を溜めておきながら発信していなかった自分より、積極的にアウトプットしているほうが素晴らしいと思います。
ぜひ今後も間違いを恐れずに発信いただければと思います!

tetsu.ktetsu.k

何というありがたいお言葉…!😭 勇気が持てました。

引き続き、積極的にアウトプットに励みます!