mcpField
P
periscope-mcp

periscope-mcp

ローカル
github.com開発ツール1015 週間前に更新

periscope-mcp について

periscope-mcpは、PlaywrightでWebアプリをテスト・監査するAIエージェント向けMCPサーバーです。ログイン後のアプリにも対応し、構造化された判定、認証セッションの保持、サイト全体の監査を提供します。

主な機能

  1. 01Playwrightによる操作とテスト
  2. 02認証セッションの保持
  3. 03サイトのクロールと一括監査
  4. 04アクセシビリティ・SEO・GEO検査
  5. 05ネットワークログ・モック・状態スナップショット
  6. 06LighthouseとWeb Vitalsの測定

活用シーン

  1. 01ログイン後の複数ステップの検証
  2. 02アクセシビリティとSEOの問題確認
  3. 03レスポンシブページのテストとレポート作成

MCP 対応

stdio経由でMCPクライアントにWebテストツールを提供し、プロジェクト設定、認証、クロール、Webアプリの操作テストを行います。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

インストール方法は未掲載です

このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。

リポジトリ

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

periscope-mcp

periscope-mcp MCP server

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_authHTTP 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_test25 種類のアクションを使った複数ステップのワークフロー(下記参照)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_screenshots2 枚のスクリーンショットをピクセル単位で比較しますscreenshot1、screenshot2
visual_check名前付きのビジュアル回帰ベースラインを設定し、厳密な合否を判定しますsession_id、name
test_responsiveモバイル/タブレット/デスクトップのビューポートでテストしますurl
check_links内部リンクと外部リンクを包括的にチェックします(url または session_id)
measure_interactionクリックから結果が表示されるまでの時間を測定しますsession_id、selector
get_table_dataHTML テーブルを構造化 JSON に解析します(ヘッダー → セルの値)session_id
get_toast_messages表示されているトースト/通知メッセージを取得しますsession_id
run_lighthouseGoogle 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_dialogJavaScript の 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_networkAPI レスポンスをモックします(エラー/空/読み込み中の状態をテストします)session_id、url_pattern
clear_interceptsネットワークのモックを削除します(すべて、またはパターンを指定)session_id
get_local_storagelocalStorage または sessionStorage を読み取りますsession_id
set_local_storagelocalStorage または sessionStorage に書き込みますsession_id、entries
select_iframeiframe のコンテンツに切り替えます(新しいセッションを返します)session_id、selector
get_computed_style実際に描画された CSS 値を取得しますsession_id、selector、properties
emulate_networkネットワークをスロットリングします:slow_3g、fast_3g、offline、resetsession_id、preset
test_dark_modeprefers-color-scheme をダーク/ライトに切り替えますsession_id、mode
download_fileトリガーをクリックして、ダウンロードしたファイルを取得します(パス、sha256、テキストのプレビュー)session_id、selector
記録とコンソール(3 個のツール)
ツール説明必須パラメーター
record_sessionワークフローを動画として記録しますurl、steps
test_keyboard_navigationTab 順序とフォーカスインジケーターを監査します(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_searchDuckDuckGo を検索します:タイトル、URL、スニペットquery
web_fetchURL を取得して読みやすい 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 を編集してデフォルト値を変更できます(環境変数で上書き可能な設定には変数名を記載しています)。

設定デフォルト説明
HEADLESSTrueChrome をヘッドレスモードで実行します(環境変数:HEADLESS=false)
STARTUP_PAUSE10ヘッドレスではないブラウザーが開いた後に待機する秒数(環境変数:STARTUP_PAUSE)
TIMEOUT30000ページ読み込みのタイムアウト(ms)
VIEWPORT_WIDTH1920ブラウザーのビューポート幅
VIEWPORT_HEIGHT1080ブラウザーのビューポート高
CHROMIUM_PATH未設定システムの Chromium バイナリへのパス(環境変数:CHROMIUM_PATH)。未設定の場合は Playwright に同梱されたビルドを使用します
WAIT_UNTILnetworkidleページ遷移の待機方法。アイドル状態にならないページ(Turnstile、WebSocket など)では、ページごとに自動的に load に切り替わり、wait_downgraded として記録されます(環境変数:NAV_WAIT_UNTIL=load を指定すると、全体でこの設定が強制されます)
MAX_PAGES20クロールするページ数のデフォルト上限
MAX_DEPTH3クロールの深さのデフォルト上限
MAX_SESSIONS20同時に実行できるインタラクティブセッションの最大数(環境変数:MAX_SESSIONS)
SESSION_TIMEOUT300アイドル状態のセッションを N 秒後に自動的に期限切れにします(環境変数:SESSION_TIMEOUT)
MAX_RESPONSE_BODY_SIZE512000レスポンスボディごとに取得する最大バイト数
MAX_RESPONSE_BODIES100セッションごとに保持するレスポンスボディの最大数
MAX_CONSOLE_LOG500セッションごとに保持するコンソールエントリの最大数
MAX_NETWORK_LOG1000セッションごとに保持するネットワークログエントリの最大数

データの保存先

すべてのデータは 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 をボリュームとしてマウントするため、スクリーンショット、レポート、プロジェクト設定はコンテナーを再起動しても保持されます。

主要な設計上の決定事項

  1. プロジェクトごとのブラウザーコンテキスト - 各プロジェクトには専用の Playwright BrowserContext が割り当てられます。これにより、プロジェクト間でセッション(Cookie、認証情報)が分離されます。

  2. ブラウザーの遅延初期化 - Playwright ブラウザーはサーバー起動時ではなく、最初のツール呼び出し時にのみ起動します。ブラウザーがクラッシュした場合や起動に失敗した場合は、次の呼び出し時に再作成されます。

  3. BFS クローリング - クローラーは深さを追跡しながら幅優先探索を行います。同じドメイン内にとどまり、ページ以外のリソース(画像、PDF など)はスキップします。

  4. チェックのモジュール化 - 各チェックカテゴリは checks/ 内の個別のモジュールです。Playwright Page を受け取り、list[dict] を返す関数を作成することで、新しいチェックを追加できます。

  5. JSON ストレージ - プロジェクトは単一の projects.json ファイルに保存されます。想定される規模(数千ではなく数十のプロジェクト)であれば、データベースは不要です。

  6. 永続セッション - インタラクティブなテストでは SessionManager を使用し、セッション ID をキーとする dict に Playwright ページを保持します。セッションはアイドルタイムアウト後に自動的に期限切れとなり、リソースリークを防ぐため、設定可能な最大数に制限されます。

  7. 一時モードとセッションモード - get_page_elements、interact_and_test、check_links などのツールは、既存のページを再利用する session_id と、使用後に閉じられる一時的なページを作成する url のどちらかを受け付けます。これにより、インタラクティブな用途にも単発の用途にも柔軟に対応できます。

新しいチェックの追加

  1. 適切な 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
  1. 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 existplaywright 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 を参照してください。

実行、改変、利用は、商用利用を含め、どこでも自由です。改変版を配布する場合、またはネットワークサービスとして提供する場合は、改変内容を同じライセンスの下で公開する必要があります。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。