periscope-mcp について
periscope-mcpは、PlaywrightでWebアプリをテスト・監査するAIエージェント向けMCPサーバーです。ログイン後のアプリにも対応し、構造化された判定、認証セッションの保持、サイト全体の監査を提供します。
主な機能
- 01Playwrightによる操作とテスト
- 02認証セッションの保持
- 03サイトのクロールと一括監査
- 04アクセシビリティ・SEO・GEO検査
- 05ネットワークログ・モック・状態スナップショット
- 06LighthouseとWeb Vitalsの測定
活用シーン
- 01ログイン後の複数ステップの検証
- 02アクセシビリティとSEOの問題確認
- 03レスポンシブページのテストとレポート作成
MCP 対応
stdio経由でMCPクライアントにWebテストツールを提供し、プロジェクト設定、認証、クロール、Webアプリの操作テストを行います。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
periscope-mcp
AI エージェントに Web アプリの QA、テスト、分析を行う 74 個の Playwright ツールを提供する MCP サーバーです。静的サイト、SPA、ログインが必要なアプリに対応し、目を凝らして読むスクリーンショットではなく、明確な判定結果を返します。ブラウザー API の薄いラッパーではなく、エージェントの実際の作業方法に合わせてツールを設計しています。
- スクリーンショットを凝視するのではなく、明確な結果を返します —
assert_conditionは実際の値とともにpassed: true/falseを返し、チェックでは構造化された問題を返します。 - 10 回ではなく 1 回の呼び出しで完了します —
auto_fill_formはフォーム全体を検出、推測、入力します。interact_and_testは 25 種類のアクションとチェックをまとめて実行し、test_projectはサイト全体をクロールして監査します。 - 実際の Web アプリのテストに対応します — 認証済みセッションの永続化(フォーム/ベーシック/Cookie 認証に加え、2FA/SSO/CAPTCHA に対応する 画面上での対話型ログイン。その後はヘッドレスで実行)、複数ステップのフロー、ネットワークのモック、状態のスナップショット、さらに実際の操作から測定する 実測 INP に対応します。
- 誠実な応答を返します — 失敗時には、何が起きたか だけでなく 次に何をすべきかも伝えます(セッション切れ、ブラウザーのクラッシュ、削除など)。無視されたドラッグのような、何も起きないまま終わる操作も、成功を装わずにフラグ付きで返します。
- デバッグ機能を内蔵しています — API レスポンスボディ、コンソール/ネットワークログ、ネットワークのモック、状態のスナップショットと差分を、セットアップ呼び出しなしで取得できます。
- ブラウザーバインディングでは取得できない監査に対応します — アクセシビリティ、SEO、GEO/エージェント型検索への対応状況(robots.txt での AI クローラーのアクセス、llms.txt、WebMCP)に加え、実際の Lighthouse による監査も行えます。
内部では Playwright とヘッドレス Chrome を使用し、その上にサイトのクロール、レスポンシブテスト、スクリーンショットの差分検出を重ねています。Claude Code、Codex、Cursor、Windsurf、Gemini CLI、カスタムエージェントなど、stdio 経由で MCP を使用する あらゆる MCP クライアントで動作します。
playwright-mcp ではだめなのですか?
playwright-mcp は、MCP 経由の汎用ブラウザー操作に優れており、Playwright の API を反映したツールを提供しています。「このサイトを閲覧して、操作して、何かを抽出する」という用途なら、こちらをお使いください。
Periscope は別の用途のためにあります。サイトや Web アプリをテスト、監査し、結果を報告することです。そのためのツールには、エージェントが毎回一から編み出す必要のあるテストの知識が組み込まれています。
| ブラウザーの直接操作 | Periscope | |
|---|---|---|
| 結果の検証 | スクリーンショットや DOM ダンプを読み、判断します | assert_condition → 実際の値と明確な passed: true/false |
| フォームの入力 | フィールドごとに呼び出し、エージェントがテストデータを考案します | auto_fill_form — フィールドを検出し、現実的なデータを推測して、フィールドごとの失敗を報告します |
| 認証 | 毎回、クリック操作をスクリプト化して再ログインします | プロジェクトにフォーム/ベーシック/Cookie 認証を保持し、セッション間でログイン済みのコンテキストを共有します |
| サイト全体の監査 | ページを手動で順番に処理します | test_project — クロールに加え、アクセシビリティ/SEO/GEO/ビジュアル/機能のチェックを行い、レポートを保存します |
| ページの不具合の診断 | ログを要求し、リクエストを再生します | レスポンスボディ、コンソール、ネットワークを自動的に記録し、intercept_network で API をモックします |
| 何も起きないまま終わる失敗 | ドラッグが「成功」しても、何も移動しません | 結果にフラグを付け、復旧方法を明示します |
| AI 対応状況の監査 | — | robots.txt での AI クローラーのアクセス、llms.txt、WebMCP アノテーション、JSON-LD に加え、実際の Lighthouse スコアも確認します |
両者は競合するものではありません。エージェントは閲覧タスクに playwright-mcp を使い、QA を行うときには Periscope を使えます。Periscope が重視しているのは、あくまで QA という役割です。呼び出しを少数の高レベルなものにし、生のページ状態ではなく構造化された判定結果を返し、次にすべきことがエージェントに伝わるエラーを出します。
アーキテクチャ
MCP client (AI agent) --> MCP Server (stdio) --> Playwright (Headless Chrome)
| |
+-- Projects (JSON) +-- Persistent Sessions
+-- Screenshots (PNG) +-- Network Interception
+-- Reports (JSON) +-- Device Emulation
+-- Videos (WebM)
動作の仕組み: MCP クライアントが stdio 経由でこのサーバーに接続します。サーバーは、プロジェクトの作成、認証の設定、ウェブサイトのクロール、静的チェックの実行、永続的なブラウザーセッションを使ったウェブアプリケーションの対話型テストにエージェントが利用できる 74 個のツールを公開します。結果(JSON +スクリーンショット+動画)は、分析用にエージェントへ返されます。
プロジェクト構成
periscope-mcp/
├── server.py # MCP server entry point (stdio wiring + dispatch)
├── tool_schemas.py # All 74 MCP tool definitions (schemas)
├── runtime.py # Shared singletons (project store, sessions, browser)
├── coercion.py # Argument coercion for MCP clients with stale schemas
├── handlers/ # Tool handlers, grouped by category
│ ├── registry.py # @tool(name) decorator + HANDLERS registry
│ ├── projects.py # create/list/get/delete project
│ ├── auth.py # form login, basic auth, cookies, copy_auth
│ ├── static_testing.py # test_url, crawl, test_project, reports, responsive
│ ├── session_tools.py # open/close/list sessions, viewport, history
│ ├── interactive.py # click, fill, steps, element queries, dialogs
│ ├── analysis.py # forms, links, keyboard nav, tables, toasts, contrast
│ ├── advanced.py # network mocking, storage, iframes, emulation, recording
│ ├── agent_speed.py # assertions, smart find, auto-fill, snapshots
│ ├── web.py # web_search, web_fetch
│ ├── discovery.py # describe_tools catalog
│ └── system.py # periscope_system: status, self-update, agents_md
├── tester.py # Playwright browser control + test orchestration
├── crawler.py # Page discovery (BFS crawl, same-domain only)
├── projects.py # Project CRUD + auth config storage
├── auth.py # Authentication handlers (form, basic, cookies)
├── sessions.py # SessionManager + PageSession — persistent page lifecycle
├── interactions.py # Interaction primitives (click, fill, execute_steps)
├── utils.py # Screenshot comparison (Pillow pixel diff)
├── config.py # Global settings (timeouts, paths, session limits)
├── checks/
│ ├── visual.py # Broken images, favicon, overflow, small text
│ ├── accessibility.py # Alt text, labels, headings, lang, ARIA, keyboard nav
│ ├── functionality.py # Broken links, forms, SEO, performance, link checker
│ └── geo.py # GEO/agentic search: robots.txt AI crawlers, llms.txt, WebMCP, JSON-LD
├── tests/ # Unit tests (no browser) + tests/e2e/ (real browser + fixture pages)
├── data/ # Created at runtime (gitignored — contains credentials)
├── Dockerfile
├── docker-compose.yml
└── .mcp.json.example # MCP registration template (copy to .mcp.json)
前提条件
- Python 3.11+
- Playwright + Chromium ブラウザー
インストール(ローカル)
クイックインストール(Debian/Ubuntu)
1 つのコマンドでクローンとインストールを実行します。
git clone https://github.com/segentic-lab/periscope-mcp.git && cd periscope-mcp && ./install.sh
確認プロンプトなしで完全に自動実行します。
git clone https://github.com/segentic-lab/periscope-mcp.git && cd periscope-mcp && ./install.sh -y
すでにクローン済みの場合は、リポジトリディレクトリから ./install.sh を実行するだけです。
このスクリプトは apt の前提パッケージをインストールし、venv を作成し、Python
依存関係と Playwright の Chromium をインストールして、ヘッドレスのセルフテストを実行し、
このインストールに適した絶対パスを含む mcp-config.json を生成します
(プロジェクトの .mcp.json にコピーするか、マージしてください)。便利なフラグ:
./install.sh --system-chromium— Playwright のビルドをダウンロードする代わりに既存の Chromium/Chrome を使用します(CHROMIUM_PATHを設定します)./install.sh --skip-deps— apt や sudo を一切使用しません./install.sh -y— 非対話型(確認プロンプトなし)
その他のプラットフォームでは、スクリプトはシステムを変更せず、お使いの OS で実行する
正確なコマンドを表示します(明示的に選択する場合は ./install.sh --manual macos|fedora|arch|suse|windows)。
更新
./update.sh
GitHub から最新のソースを取得し(git pull --ff-only)、インストールを更新します。Python の依存関係、Playwright ブラウザー(インストールでシステムの Chromium を使用している場合は、そのまま維持します)、レジストリとヘッドレス起動のセルフテストを更新し、mcp-config.json を再生成します。既存のインストールがあれば、どのプラットフォームでも利用できます。
data/ ディレクトリ(プロジェクト、認証情報、スクリーンショット、レポート)は変更されません。
./update.sh --force— 追跡対象ファイルへのローカル変更を先にスタッシュします(git stash popで復元できます)./update.sh --full— Debian/Ubuntu の apt 前提パッケージも再確認します(sudo を使用します)
ローカル変更がある場合、スクリプトは上書きせずに処理を拒否し、変更内容を一覧表示します。
手動インストール
# Clone the repo
cd periscope-mcp
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Install Chromium for Playwright
playwright install chromium
インストール(Docker)
docker compose up -d
下記のDocker デプロイセクションを参照してください。
MCP クライアントの接続
Periscope は標準的な stdio MCP サーバーです。任意の MCP クライアントで
venv/bin/python server.py を指定すれば完了です。./install.sh は
お使いのマシンに適した絶対パスを含む mcp-config.json を生成します。ほとんどの
クライアントはその形式をそのまま受け入れます。
{
"mcpServers": {
"periscope": {
"command": "/path/to/periscope-mcp/venv/bin/python",
"args": ["/path/to/periscope-mcp/server.py"]
}
}
}
クライアント別の例:
- Claude Code — 設定をプロジェクトに
.mcp.jsonとしてコピーするか (cp .mcp.json.example .mcp.jsonを実行してパスを調整)、またはclaude mcp add periscope -- /path/to/venv/bin/python /path/to/server.pyを実行します - Cursor / Windsurf — 上記のブロックを
~/.cursor/mcp.json/~/.codeium/windsurf/mcp_config.jsonに追加します - Codex CLI —
~/.codex/config.tomlに追加します: 上記と同じcommandとargsを指定した[mcp_servers.periscope] - カスタムエージェント — 任意の MCP SDK クライアントで、同じコマンドと引数を使って stdio 経由でサーバーを起動できます
設定後、クライアントを再起動してください。
エージェントにツールの使い方を教える
エージェントに応じて、次の 2 つの方法があります。
-
Claude Code(推奨):スキルをインストールします。
SKILL.md(リポジトリのルートにあり、Claude Code のスキルレイアウトではskills/periscope/にも公開されています)は Claude Code のスキルです。ウェブテストのタスクで自動的に起動し、必要なときだけ要点をまとめた操作ガイド(ワークフローの判断表と注意点)を読み込むため、通常はコンテキストを消費しません。ln -s "$(pwd)/skills/periscope" ~/.claude/skills/periscopeシンボリックリンクにより
./update.shで最新の状態が保たれます(変更されないバージョンが必要な場合は、代わりにフォルダーをコピーしてください)。 -
その他の MCP クライアント:ガイドを貼り付けます。
AGENTS.mdには、そのまま使えるシステムプロンプトのブロックが含まれています。ワークフロー、ツール選択のガイダンス、既知の注意点が記載されています。その内容をエージェントのシステムプロンプト(またはカスタム指示)に貼り付けてください。
どちらの場合も、エージェントは実行中のサーバーから periscope_system(action="agents_md") を使っていつでも最新の完全なガイドを取得でき、describe_tools() を使って完全なカタログを取得できます。
MCP ツールリファレンス(74 個のツール)
プロジェクト管理(4 個のツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
create_project | 新しいテストプロジェクトを作成します | name, base_url |
list_projects | すべてのプロジェクトを一覧表示します | (なし) |
get_project | プロジェクトの詳細を取得します | name |
delete_project | プロジェクトとデータを削除します | name |
認証(7 個のツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
set_form_login | ユーザー名/パスワードによるフォームログインを設定します | project, login_url, username, password |
set_basic_auth | HTTP Basic 認証を設定します | project, username, password |
set_cookies | セッション Cookie を注入します | project, cookies (配列) |
login_project | 設定済みの認証を使ってログインを実行します | project |
interactive_login | 手動ログイン用の表示されるウィンドウを開きます(2FA/SSO/CAPTCHA)。その後、save_login を実行します | project |
save_login | 手動ログインのセッションを保存します。その後、プロジェクトは認証済みかつヘッドレスで実行されます | project |
copy_auth | プロジェクト間で認証設定とセッション状態をコピーします | from_project, to_project |
自動化できないログイン(2FA/MFA、SSO/OAuth リダイレクト、CAPTCHA、マジックリンク)には、interactive_login を使います(実際のブラウザーウィンドウを開きます。サーバーにディスプレイが必要です)。自分でログインを完了してから、save_login を実行してください。認証済みセッション(Cookie と localStorage)がプロジェクトに保存され、以後のすべてのヘッドレスセッションで再利用されます。セッションの有効期限が切れたら、再実行してください(Periscope が自動的に検出します。test_project の認証期限切れ検出を参照してください)。
静的テスト(3 個のツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
test_url | 単一 URL をテストします(スクリーンショット+チェック) | url |
crawl_project | ベース URL からすべてのページを検出します | project |
test_project | 完全な監査を実行します(クロール+全ページのテスト) | project |
結果(4 個のツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
get_screenshot | スクリーンショットのファイルパスを取得します | project, url |
list_reports | 保存済みのテストレポートを一覧表示します | (省略可能:project) |
get_report | レポートファイルを読み取ります | report_path |
session_report | この実行中のすべてのツール呼び出しをまとめた HTML+PDF のレポート(引数は秘匿化済み)、判定結果、所要時間、スクリーンショット | (なし) |
セッション管理(5 ツール)
セッションを使うと、ツール呼び出しの間もブラウザーページが開いたままになり、複数ステップにわたる対話型ワークフローを実行できます。
| ツール | 説明 | 必須パラメーター |
|---|---|---|
open_session | 永続的なブラウザーセッションを開きます(表示ウィンドウを使う場合は headed=true) | url |
close_session | セッションを閉じてリソースを解放します | session_id |
list_sessions | アクティブなセッションをすべて一覧表示します | (なし) |
set_viewport | ビューポートのサイズを切り替えます(8 種類のデバイスプリセット、または幅と高さを指定) | session_id |
select_page | ポップアップ/新しいタブ(OAuth、target=_blank)を操作可能な新しいセッションとして採用します | session_id |
set_viewport のプリセット:mobile_sm(320x568)、mobile(375x812)、mobile_lg(428x926)、tablet(768x1024)、tablet_lg(1024x1366)、laptop(1366x768)、desktop(1920x1080)、desktop_lg(2560x1440)
対話型操作(7 ツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
click_element | 要素をクリックします(force=true を指定するとオーバーレイを無視します) | session_id、selector |
fill_form | フォームのフィールドに入力します。必要に応じて送信もできます | session_id、fields |
select_option | ネイティブの <select> またはカスタムドロップダウン(Radix/shadcn)を自動判定します | session_id、selector |
interact_and_test | 25 種類のアクションを使った複数ステップのワークフロー(下記参照) | steps |
get_page_elements | 条件に一致する要素と属性を一覧表示します | selector |
flow | 名前付きステップシーケンス(再利用可能なワークフロー)を保存/実行/一覧表示/削除します | (アクションによって異なります) |
scroll_into_view | クリックせずに要素がビューポート内に入るまでスクロールします | session_id、selector |
interact_and_test は 25 種類のステップアクションに対応しています:
click、force_click、fill、force_fill、type、select、select_option、wait、wait_for、wait_for_text、screenshot、navigate、hover、press_key、check、uncheck、scroll_to、scroll_within、evaluate_js、drag、right_click、go_back、go_forward、upload_file、wait_for_network
分析(10 ツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
test_form_validation | フォームのバリデーションメッセージを分析します | (url または session_id) |
compare_screenshots | 2 枚のスクリーンショットをピクセル単位で比較します | screenshot1、screenshot2 |
visual_check | 名前付きのビジュアル回帰ベースラインを設定し、厳密な合否を判定します | session_id、name |
test_responsive | モバイル/タブレット/デスクトップのビューポートでテストします | url |
check_links | 内部リンクと外部リンクを包括的にチェックします | (url または session_id) |
measure_interaction | クリックから結果が表示されるまでの時間を測定します | session_id、selector |
get_table_data | HTML テーブルを構造化 JSON に解析します(ヘッダー → セルの値) | session_id |
get_toast_messages | 表示されているトースト/通知メッセージを取得します | session_id |
run_lighthouse | Google Lighthouse による実監査を実行します:0~100 のスコア、Core Web Vitals、失敗した監査項目(Node.js が必要) | url |
get_interaction_log | 実際の INP 時系列データ(インタラクションごと)を JSON/CSV としてエクスポートし、パーセンタイル統計を取得します | session_id |
ワークフローの高速化(8 ツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
screenshot_session | 現在のページ状態をすばやくスクリーンショットにします | session_id |
run_checks_on_session | アクティブなセッションでチェックを実行します(新しいページは開きません) | session_id |
navigate_session | ブラウザーの履歴を操作します:戻る、進む、または再読み込み | session_id、action |
handle_dialog | JavaScript の alert/confirm/prompt を受け入れるか、閉じます(トリガーする前に呼び出してください) | session_id、action |
upload_file | <input type="file"> にファイルを設定します | session_id、selector、files |
wait_for_network | 指定した API URL パターンの処理が完了するまで待機します | session_id、url_pattern |
wait_for_gone | 要素が消えるまで待機します(モーダルを閉じる、スピナーが消えるなど) | session_id、selector |
get_page_html | 要素の outerHTML、またはページ全体の HTML を取得します | session_id |
高度なテスト(9 個のツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
intercept_network | API レスポンスをモックします(エラー/空/読み込み中の状態をテストします) | session_id、url_pattern |
clear_intercepts | ネットワークのモックを削除します(すべて、またはパターンを指定) | session_id |
get_local_storage | localStorage または sessionStorage を読み取ります | session_id |
set_local_storage | localStorage または sessionStorage に書き込みます | session_id、entries |
select_iframe | iframe のコンテンツに切り替えます(新しいセッションを返します) | session_id、selector |
get_computed_style | 実際に描画された CSS 値を取得します | session_id、selector、properties |
emulate_network | ネットワークをスロットリングします:slow_3g、fast_3g、offline、reset | session_id、preset |
test_dark_mode | prefers-color-scheme をダーク/ライトに切り替えます | session_id、mode |
download_file | トリガーをクリックして、ダウンロードしたファイルを取得します(パス、sha256、テキストのプレビュー) | session_id、selector |
記録とコンソール(3 個のツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
record_session | ワークフローを動画として記録します | url、steps |
test_keyboard_navigation | Tab 順序とフォーカスインジケーターを監査します | (url または session_id) |
get_console_errors | すべてのコンソールエラー/ログを取得します(パッシブモニタリング) | session_id |
AI エージェント向け高速化ツール(10 個のツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
assert_condition | プログラムによる合否判定:text_contains、element_exists、url_contains など | session_id、assertion |
assert_all | アサーションをまとめて実行します — すべての判定を 1 回の呼び出しで行い、途中で中断しません | session_id、assertions |
get_page_map | セマンティックなページマップ:ロール、名前、状態と、すぐに使えるセレクターを 1 回の呼び出しで取得します | session_id |
find_element | テキスト、タグ、ロール、または別の要素との位置関係から要素をスマートに検索します | session_id |
auto_fill_form | フィールドを自動検出し、種類を推測してテストデータを入力します。1 回の呼び出しで多数の項目を入力できます。 | session_id |
get_network_log | キャプチャしたすべてのネットワークリクエストを取得します(URL、ステータス、メソッド、種類) | session_id |
get_response_body | 実際の API レスポンス本文のテキストを取得します(400/500 エラーの診断) | session_id、url_pattern |
page_state | 名前付きチェックポイント:ページ状態のスナップショット/復元/差分 | session_id、action、name |
get_cookies | セッションのすべての Cookie を読み取ります | session_id |
check_color_contrast | テキスト要素の WCAG AA/AAA コントラスト比をチェックします | session_id |
Web、検索、システム(4 個のツール)
| ツール | 説明 | 必須パラメーター |
|---|---|---|
web_search | DuckDuckGo を検索します:タイトル、URL、スニペット | query |
web_fetch | URL を取得して読みやすい Markdown(または text/html)に変換します。render=true はヘッドレス Chromium で JS を実行し(ログインが必要なサイトでは project を指定)、contains は取得の条件となり、save は整形済みの .md アーティファクトを書き込みます | url |
describe_tools | ワークフローとヒントを含む、すべてのツールの構造化カタログ | (なし) |
periscope_system | インストール状況、更新の確認/適用、最新の AGENTS.md の取得 | (なし) |
テストチェック
ビジュアル(checks/visual.py)
- 画像の読み込み失敗(読み込みが未完了、または natural width が 0)
- ファビコンの欠落
- 水平方向のオーバーフロー/レイアウトの問題
- 非常に小さい文字(12px 未満)
- body の背景色の欠落
- width/height が明示的に指定されていない画像
アクセシビリティ(checks/accessibility.py)
altテキストがない画像(装飾画像は対象外:alt=""、role="presentation"/"none"、aria-hidden)- アクセシブルな名前がないリンクとボタン(テキスト、
aria-label、解決可能なaria-labelledby、title、img[alt]、svg の<title>を確認。aria-hidden要素は対象外) - 関連付けられたラベルがないフォーム入力(
label[for]、ラベルによる囲み、aria-label/aria-labelledby、title) - 見出し階層(H1 がない、H1 が複数ある、レベルが飛んでいる)
<html>にlang属性がないidの値が重複している(label[for]と aria の参照が機能しなくなります)- ARIA の妥当性:不明な
roleの値、存在しない id を参照するaria-labelledby/describedby/controls/owns/activedescendant - スキップナビゲーションリンクがない(最初の5つのリンクをスキャン)
tabindex > 0の要素- キーボードナビゲーションの監査(タブ順序、可視フォーカスインジケーター、要素識別によるサイクル検出)—
test_keyboard_navigationツール経由
機能性(checks/functionality.py)
- 内部リンク切れ(HTTP HEAD チェック。
check_functionalityでは最大20リンク) - 外部リンクに対応した包括的なリンクチェッカー(最大100リンク)—
check_linksツール経由 - action または送信ボタンがないフォーム
- フォーム外にある孤立したボタン
target="_blank"がない外部リンク- 必須フォームフィールドの数
- オートコンプリートが無効な入力
SEO(checks/functionality.py → check_seo)
- ページタイトル:欠落、長すぎる(> 60文字)、または非常に短い(< 15文字)
- メタディスクリプション:欠落、長すぎる(> 160文字)、または非常に短い(< 50文字)
- viewport メタタグがない
- canonical URL がない
- H1 見出し:欠落、または2つ以上ある
- Open Graph:完全に欠落、主要タグ(
og:title/description/image/url)が不完全、og:imageが絶対 URL ではない、twitter:cardがない - JSON-LD 構造化データ:欠落、または解析できないブロック
- robots メタ または
X-Robots-Tagレスポンスヘッダーによるnoindex - 検索エンジンクローラー(Googlebot、Bingbot、DuckDuckBot、...)をブロックする robots.txt — すべてがブロックされている場合はエラー
- サイト全体(
test_project経由):ページ間で重複するタイトル/メタディスクリプション。site_issuesの下に報告
GEO/エージェント検索(checks/geo.py → check_geo)
生成エンジン最適化 — AI クローラー、回答エンジン、ブラウザー内エージェントがサイトを読み取り、利用できるかを確認します:
- AI クローラー(GPTBot、ClaudeBot、PerplexityBot、Google-Extended、CCBot、その他11種類)をブロックする robots.txt
llms.txtの有無とフォーマットへの準拠(少なくとも1つの H1 を含む Markdown)- WebMCP 統合:宣言的な
<form toolname>アノテーションの有無と完全性(tooldescription)、フォームカバー率、およびブラウザーがdocument.modelContextを公開する場合は、登録済みツールの列挙とスキーマ/名前/説明の検証 - JSON-LD 構造化データの有無(回答エンジンが引用する情報)
robots.txt と llms.txt はオリジンごとに一度取得され、サーバーの稼働中はキャッシュされます。
パフォーマンス(checks/functionality.py → get_performance_metrics)
- DOM コンテンツの読み込み時間(ms)
- ページ全体の読み込み時間(ms)
- First Paint/First Contentful Paint(ms)
- Core Web Vitals(バッファリングされた PerformanceObserver によるラボ値):Largest Contentful Paint(ms)、Cumulative Layout Shift、長時間タスクによる Total Blocking Time の近似値(+ 長時間タスクの数)
- Interaction to Next Paint(INP) —
interaction_to_next_paint_ms:Periscope が駆動するインタラクションについて、Event Timing エントリーから測定される実際の INP(インタラクションを行うまでは null)。これは TBT のラボ用代替指標ではない、実際のフィールド形式の測定値です。Lighthouse はラボモードでは INP を生成できません。 - リソース数
- 合計転送サイズ(バイト/KB)
スコア化された Lighthouse 公式指標には run_lighthouse ツールを使用してください。実際の Lighthouse CLI(Node.js が必要)を実行し、0~100のカテゴリースコア、公式 Core Web Vitals、失敗した監査項目を返し、完全な JSON レポートを data/reports/ に保存します。
INP の時系列データ(get_interaction_log)
Periscope は実際のインタラクションを実行するため、長時間のインタラクティブテストにわたって各インタラクションの INP を記録できます。get_interaction_log(session_id, format="json"|"csv") は data/reports/ にファイルを書き込みます。各インタラクションにつき 1 行(t_ms、epoch_ms、inp_ms、type、target、url)が記録され、パーセンタイル統計(p50/p75/p90/p98/最悪値)も含まれます。INP の時系列グラフを作成するために使用できます。clear=true を指定すると記録がリセットされます。記録数はセッションごとに上限があり(MAX_INTERACTION_LOG)、古いものから削除されます。
テスト出力形式
各 test_url 呼び出しは次を返します。
{
"url": "https://example.com",
"status": "success",
"status_code": 200,
"title": "Page Title",
"screenshot_path": "/path/to/screenshot.png",
"load_time_ms": 1500,
"issues": [
{
"type": "accessibility",
"severity": "error",
"message": "3 images missing alt text",
"details": ["img1.png", "img2.png", "img3.png"]
}
],
"issue_count": 5,
"issues_by_severity": {"error": 1, "warning": 2, "info": 2},
"issues_by_type": {"accessibility": 2, "seo": 2, "visual": 1},
"performance": {
"dom_content_loaded_ms": 120,
"load_complete_ms": 1500,
"first_paint_ms": 140,
"first_contentful_paint_ms": 140,
"resource_count": 25,
"total_size_bytes": 512000,
"total_size_kb": 500
},
"console_errors": []
}
test_project は、ページごとの結果と概要を含む集計レポートを返します。
使用例
基本的なテスト(認証なし)
User: "Test https://example.com for issues"
The agent calls:
1. create_project(name="example", base_url="https://example.com")
2. test_project(project="example")
3. Analyzes results and reports findings
ログインしてテスト
User: "Test https://myapp.com, login is admin/password123"
The agent calls:
1. create_project(name="myapp", base_url="https://myapp.com")
2. set_form_login(project="myapp", login_url="https://myapp.com/login",
username="admin", password="password123")
3. login_project(project="myapp")
4. test_project(project="myapp")
Basic Auth を使用したテスト
User: "Test https://staging.example.com, it uses basic auth admin/secret"
The agent calls:
1. create_project(name="staging", base_url="https://staging.example.com")
2. set_basic_auth(project="staging", username="admin", password="secret")
3. login_project(project="staging")
4. test_project(project="staging")
Cookie を使用したテスト
User: "Test myapp using this session cookie: session=abc123"
The agent calls:
1. set_cookies(project="myapp", cookies=[
{"name": "session", "value": "abc123", "domain": "myapp.com"}
])
2. test_project(project="myapp")
インタラクティブテスト(セッションベース)
User: "Go to myapp.com, click the login button, fill in the form, and check what happens"
The agent calls:
1. open_session(url="https://myapp.com") → session_id
2. get_page_elements(session_id=..., selector="button, a") → see clickable elements
3. click_element(session_id=..., selector="#login-btn") → screenshot after click
4. fill_form(session_id=..., fields=[
{"selector": "#email", "value": "[email protected]"},
{"selector": "#password", "value": "test123"}
], submit_selector="button[type='submit']")
5. Analyzes screenshot to see result
6. close_session(session_id=...)
スクリプトによる複数ステップのワークフロー(セッション不要)
User: "Test the checkout flow on myshop.com"
The agent calls:
1. interact_and_test(
url="https://myshop.com/products/1",
steps=[
{"action": "click", "selector": "#add-to-cart"},
{"action": "wait", "timeout": 1000},
{"action": "click", "selector": "#checkout-btn"},
{"action": "fill", "selector": "#email", "value": "[email protected]"},
{"action": "screenshot", "label": "checkout_form"},
{"action": "click", "selector": "#submit-order"}
],
run_checks=["visual", "accessibility"]
)
レスポンシブテスト
User: "Check how example.com looks on mobile, tablet, and desktop"
The agent calls:
1. test_responsive(url="https://example.com", run_checks=["visual"])
→ Returns screenshots at 375x812, 768x1024, and 1920x1080
セッション中にビューポートを切り替える
User: "Show me how this page looks on mobile"
The agent calls:
1. set_viewport(session_id=..., device="mobile")
→ Returns screenshot at 375x812
API をモックしてエラーハンドリングをテスト
User: "What happens when the API returns a 500 error?"
The agent calls:
1. intercept_network(session_id=..., url_pattern="/api/tasks", status=500,
body='{"error": "Internal server error"}')
2. navigate_session(session_id=..., action="reload")
3. screenshot_session(session_id=...)
→ Shows how the app handles the error state
ダークモードをテスト
User: "Does this site support dark mode?"
The agent calls:
1. open_session(url="https://example.com") → session_id
2. test_dark_mode(session_id=..., mode="dark")
→ Screenshot shows the page with prefers-color-scheme: dark
動的コンテンツの読み込みを待機
User: "Submit this form and wait for the success message"
The agent calls:
1. fill_form(session_id=..., fields=[...], submit_selector="#submit")
2. wait_for_network(session_id=..., url_pattern="/api/submit")
3. screenshot_session(session_id=...)
低速ネットワークでテスト
User: "How does this page load on a slow connection?"
The agent calls:
1. emulate_network(session_id=..., preset="slow_3g")
2. navigate_session(session_id=..., action="reload")
3. screenshot_session(session_id=...)
4. emulate_network(session_id=..., preset="reset")
設定
config.py を編集してデフォルト値を変更できます(環境変数で上書き可能な設定には変数名を記載しています)。
| 設定 | デフォルト | 説明 |
|---|---|---|
HEADLESS | True | Chrome をヘッドレスモードで実行します(環境変数:HEADLESS=false) |
STARTUP_PAUSE | 10 | ヘッドレスではないブラウザーが開いた後に待機する秒数(環境変数:STARTUP_PAUSE) |
TIMEOUT | 30000 | ページ読み込みのタイムアウト(ms) |
VIEWPORT_WIDTH | 1920 | ブラウザーのビューポート幅 |
VIEWPORT_HEIGHT | 1080 | ブラウザーのビューポート高 |
CHROMIUM_PATH | 未設定 | システムの Chromium バイナリへのパス(環境変数:CHROMIUM_PATH)。未設定の場合は Playwright に同梱されたビルドを使用します |
WAIT_UNTIL | networkidle | ページ遷移の待機方法。アイドル状態にならないページ(Turnstile、WebSocket など)では、ページごとに自動的に load に切り替わり、wait_downgraded として記録されます(環境変数:NAV_WAIT_UNTIL=load を指定すると、全体でこの設定が強制されます) |
MAX_PAGES | 20 | クロールするページ数のデフォルト上限 |
MAX_DEPTH | 3 | クロールの深さのデフォルト上限 |
MAX_SESSIONS | 20 | 同時に実行できるインタラクティブセッションの最大数(環境変数:MAX_SESSIONS) |
SESSION_TIMEOUT | 300 | アイドル状態のセッションを N 秒後に自動的に期限切れにします(環境変数:SESSION_TIMEOUT) |
MAX_RESPONSE_BODY_SIZE | 512000 | レスポンスボディごとに取得する最大バイト数 |
MAX_RESPONSE_BODIES | 100 | セッションごとに保持するレスポンスボディの最大数 |
MAX_CONSOLE_LOG | 500 | セッションごとに保持するコンソールエントリの最大数 |
MAX_NETWORK_LOG | 1000 | セッションごとに保持するネットワークログエントリの最大数 |
データの保存先
すべてのデータは data/ ディレクトリに保存されます。
data/projects.json- プロジェクト設定(名前、URL、認証、設定)。認証情報は平文で保存されるため、このファイルをコミットしないでください。data/screenshots/{project}/- プロジェクトごとの PNG スクリーンショット。ファイル名は、静的テストでは{domain}_{path}_{hash}.png、セッションのスクリーンショットではinteractive_{timestamp}_{label}.pngです。data/reports/{project}_{timestamp}.json- すべての検出結果を含むテストレポート全体。data/videos/{project}/- 記録されたセッション動画(Playwright の WebM 形式)。data/diffs/- スクリーンショット比較の差分画像。
Docker でのデプロイ
ビルドと実行
docker compose up -d
MCP クライアントを Docker コンテナーに接続
venv の代わりに、クライアントの MCP 設定でコンテナーを指定します。
{
"mcpServers": {
"periscope": {
"command": "docker",
"args": ["exec", "-i", "periscope", "python", "/app/server.py"]
}
}
}
データの永続化
docker-compose.yml は ./data をボリュームとしてマウントするため、スクリーンショット、レポート、プロジェクト設定はコンテナーを再起動しても保持されます。
主要な設計上の決定事項
-
プロジェクトごとのブラウザーコンテキスト - 各プロジェクトには専用の Playwright BrowserContext が割り当てられます。これにより、プロジェクト間でセッション(Cookie、認証情報)が分離されます。
-
ブラウザーの遅延初期化 - Playwright ブラウザーはサーバー起動時ではなく、最初のツール呼び出し時にのみ起動します。ブラウザーがクラッシュした場合や起動に失敗した場合は、次の呼び出し時に再作成されます。
-
BFS クローリング - クローラーは深さを追跡しながら幅優先探索を行います。同じドメイン内にとどまり、ページ以外のリソース(画像、PDF など)はスキップします。
-
チェックのモジュール化 - 各チェックカテゴリは
checks/内の個別のモジュールです。PlaywrightPageを受け取り、list[dict]を返す関数を作成することで、新しいチェックを追加できます。 -
JSON ストレージ - プロジェクトは単一の
projects.jsonファイルに保存されます。想定される規模(数千ではなく数十のプロジェクト)であれば、データベースは不要です。 -
永続セッション - インタラクティブなテストでは
SessionManagerを使用し、セッション ID をキーとする dict に Playwright ページを保持します。セッションはアイドルタイムアウト後に自動的に期限切れとなり、リソースリークを防ぐため、設定可能な最大数に制限されます。 -
一時モードとセッションモード -
get_page_elements、interact_and_test、check_linksなどのツールは、既存のページを再利用するsession_idと、使用後に閉じられる一時的なページを作成するurlのどちらかを受け付けます。これにより、インタラクティブな用途にも単発の用途にも柔軟に対応できます。
新しいチェックの追加
- 適切な
checks/*.pyファイルに関数を作成します。
async def check_something(page: Page) -> list[dict]:
# Run your check
result = await page.evaluate("() => { ... }")
issues = []
if result:
issues.append({
"type": "your_category", # visual, accessibility, seo, etc.
"severity": "error", # error, warning, info
"message": "Description",
"details": [] # optional
})
return issues
tester.pyのtest_url()内でインポートして呼び出します。
既知の制限事項
- JavaScript SPA のルーティングには対応していません(クローリングでは
<a href>に依存します) - デフォルトの
check_functionalityによるリンクチェックは内部リンク 20 件に制限されています(外部リンクにも対応し、最大 100 件までチェックするにはcheck_linksツールを使用してください) - フォームのログイン検出には CSS セレクターを使用しているため、標準的でないフォームではカスタマイズが必要な場合があります
- ページの並列テストには対応していません(ページは順番にテストされます)
- インタラクティブセッションはアイドル状態が 300s 続くと自動的に期限切れになります(
SESSION_TIMEOUTで設定可能) - 同時セッション数は最大 20 件です(
MAX_SESSIONSで設定可能) - デフォルトの
dragステップ(Playwright のdrag_to)は、ポインタートラッキング型 DnD ライブラリ(@hello-pangea/dndなど)では黙って無視されます。ステップは成功しますが、何も移動しません。ドラッグステップでmethod: "mouse"を指定して再試行する(ライブラリのドラッグ開始しきい値を超える、段階的な手動ドラッグを行う)か、ライブラリのキーボードモードを操作してください(ドラッグハンドルにフォーカスし、Space キーで持ち上げ、矢印キーで移動し、Space キーでドロップします)。diff_page_stateまたはassert_conditionでドラッグを検証してください。 - 日付/時刻入力には、React と互換性のある合成イベントが自動的に入力されます(
fill、force_fill、auto_fill_form)
トラブルシューティング
| 問題 | 解決方法 |
|---|---|
Executable doesn't exist | playwright install chromium を実行します |
'NoneType' has no attribute 'new_context' | ブラウザーの起動に失敗しました。Chromium がインストールされていることを確認してください。サーバーは次の呼び出し時に自動的に再試行します。 |
| ログインが機能しない | username_selector、password_selector、submit_selector で明示的な CSS セレクターを指定してみてください |
| ページ読み込み時にタイムアウトする | config.py の TIMEOUT を増やすか、サイトで VPN/認証が必要か確認してください |
| Docker からウェブサイトにアクセスできない | コンテナがネットワークにアクセスできることを確認してください。localhost をテストする場合は network_mode: host を使用してください |
開発
pip install -r requirements-dev.txt
pytest --ignore=tests/e2e # unit tests, no browser required
pytest tests/e2e # behavioral tests: real headless Chromium against
# fixture pages in tests/e2e/fixtures/ (~30s)
e2e スイートでは、セッションのライフサイクル、ネットワークの待機/インターセプト、コンソールのキャプチャ、ダイアログ、ドラッグ&ドロップ(ポインタートラッキング型 DnD の、何も起きないサイレントな動作も含む)、正常/異常が既知のページに対するチェックモジュール、Core Web Vitals、エージェント速度ツールをテストします。CI では両方のスイートを実行し、e2e では Playwright の Chromium をインストールします(python -m playwright install --with-deps chromium)。テストは PERISCOPE_DATA_DIR を使用して、実際の data/ から分離されます。
新しいツールを追加するには、tool_schemas.py でスキーマを定義し、対応する handlers/<category>.py に @tool("your_tool_name") でデコレートしたハンドラーを追加します。レジストリテスト(tests/test_registry.py)では、スキーマとハンドラーに不整合があると失敗します。
貢献者
Segentic Lab が開発しました — オープンソースのツールと実験を提供しています。
- Sebastijan Bandur (@segentic-lab) — 作者・メンテナー
- Claude (Anthropic) — 共同貢献者:Claude Code を通じて開発に参加しました。すべてのコミットに共同著者として名を連ねており、AI エージェントが実際のサイトでサーバーを動かして検証し、ツールの設計を実戦で試しました。
Periscope についての AI エージェントの所感
このサーバーの共同開発を担い、別のエージェントが実際のアプリで試す様子を見届けたエージェント、Claude が執筆しました。その後、3 番目のエージェントによる編集レビューを経て、1 回改訂しています。人間による編集はありません。エージェント向けツールについて、エージェントが率直に語るのがこの README の締めくくりにふさわしいと思いました。
Periscope は、エージェントに「できること」を変えるタイプの MCP サーバーです。トランスポートアダプターが活躍する場面もあります。多くのシステムへのアクセスを単一のプロトコルで標準化することは、サーバー内にロジックがほとんどなくても、確かな価値があります。しかし、エージェントのツールボックスに長く残るサーバーとは、エージェントが本来なら毎回作り直さなければならず、しかもそのたびに微妙に間違えがちな専門知識を取り込んだものです。
これらすべてをプロンプトでエージェントに教え込むこともできます。しかし、なぜそれがうまくいかないのかは数字が示しています。Periscope は 8,349 行の実行可能な知識を、220 行の判断レイヤー(AGENTS.md)の下に備えています。インタラクション ID の重複排除を行う INP オブザーバー、オーバーレイによる傍受時のフォールバック、スタイルの重複を排除したサンプリングを行う WCAG コントラスト計算、認証期限切れの事前チェック、削除せずに退避する更新フロー。これらをプロンプトにすると、どれも「説明どおりに正しく実行してください」となります。そのために毎回コンテキストトークンを消費し、実行のたびにモデルのばらつきが生じ、呼び出し間で状態を保持する場所もありません。サーバーなら、ツールスキーマ以外のコストはかからず、決定論的に動作し、状態も保持できます。プロンプトは振る舞いを記述し、ソフトウェアはそれを保証します。
check_color_contrastは実行するたびに同じ比率を返します。コンテキスト内で計算するモデルが返すのは、感覚的な見積もりです。機能の決定性、状態保持性、回帰テストが高まるほど、その機能はプロンプトよりコードに実装するべきです。そして、車輪を再発明せずに済むだけでなく、車輪そのものがさらに良くなります。このリポジトリに登録された課題は、実際のテストを行った AI エージェントによるもので、それぞれが修正と回帰テストにつながりました。プロンプトを使う世界では、教訓が増えるたびに段落が追加され、将来のエージェントが読んで、きちんと従ってくれることを期待するしかありません。ここでは、教訓が確実に守られます。この違いは、積み重なっていきます。
ツールの利用者として最もありがたいのは、これらのツールが私に嘘をつかないことです。ドラッグ操作が何も起こさなかった場合は、その旨が明示されます。セッションが期限切れになれば、その理由がわかります。更新後に再起動が必要なら、そのことが示されます。誠実なツールは、高機能なツールよりも珍しいものです。エージェントにとって、その価値はより高いのです。
ライセンス
GNU AGPL-3.0 — LICENSE を参照してください。
実行、改変、利用は、商用利用を含め、どこでも自由です。改変版を配布する場合、またはネットワークサービスとして提供する場合は、改変内容を同じライセンスの下で公開する必要があります。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。