mcpField
P
periscope-mcp

periscope-mcp

本地服务
github.com开发工具1015 周前更新

关于 periscope-mcp

periscope-mcp 是面向 AI Agent 的网页应用 QA 与审计 MCP 服务,基于 Playwright 测试静态站点、SPA 和登录后应用。它提供结构化测试结论、持久化认证会话及网站级审计。

主要功能

  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 应用。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

periscope-mcp

periscope-mcp MCP server

一款 MCP 服务,为 AI Agent 提供 74 个 Playwright 工具,用于对 Web 应用进行 QA、测试和分析——包括静态网站、SPA 和需要登录的应用;返回明确的判定结果,而不是让人眯着眼看的截图。它并非浏览器 API 的简单封装;这些工具的设计贴合 Agent 的实际工作方式:

  • 明确结果,不必盯着截图猜测——assert_condition 会返回 passed: true/false 及实际值;检查会返回结构化问题。
  • 一次调用,胜过十次调用——auto_fill_form 可检测、推断并填写整个表单;interact_and_test 将 25 种操作类型与检查批量处理;test_project 可爬取并审计整个网站。
  • 真实的 Web 应用测试——持久化的已认证会话(表单/基本/Cookie 身份验证,以及用于 2FA/SSO/CAPTCHA 的可见交互式登录,之后以无头模式运行)、多步骤流程、网络模拟、状态快照,以及根据实际驱动的交互测得的真实 INP。
  • 如实反馈——失败信息会说明发生了什么以及接下来该做什么(会话过期、浏览器崩溃或会话被逐出);忽略拖动等无声无效操作会被标记出来,而不会伪装成成功。
  • 内置调试功能——自动捕获 API 响应正文、控制台/网络日志、网络模拟以及状态快照/差异,无需额外设置调用。
  • 浏览器绑定无法提供的审计能力——无障碍、SEO 和 GEO/Agentic 搜索就绪度(robots.txt AI 爬虫访问权限、llms.txt、WebMCP),以及真实 Lighthouse 测试。

底层使用 Playwright + 无头 Chrome;上层提供网站爬取、响应式测试和截图差异比较。兼容任何 MCP 客户端——Claude Code、Codex、Cursor、Windsurf、Gemini CLI、自定义 Agent,或任何通过 stdio 使用 MCP 的工具。

为什么不直接用 playwright-mcp?

playwright-mcp 非常擅长它的本职工作:通过 MCP 控制通用浏览器,提供与 Playwright 自身 API 对应的工具。如果任务是“浏览这个网站、点点看、提取内容”,就用它。

Periscope 面向不同的任务:测试和审计网站或 Web 应用,然后报告发现的问题——它的工具内置了测试知识,否则 Agent 每次都得重新摸索:

原始浏览器控制Periscope
验证结果阅读截图或 DOM 转储并自行判断assert_condition → 明确的 passed: true/false + 实际值
填写表单每个字段调用一次,测试数据由 Agent 自行编造auto_fill_form——检测字段、推断真实数据,并逐字段报告失败
身份验证每次会话都通过脚本模拟点击重新登录项目会持久保存表单/基本/Cookie 身份验证;会话共享已登录的上下文
全站审计手动逐页循环test_project——爬取 + 无障碍/SEO/GEO/视觉/功能检查 + 保存报告
诊断故障页面请求日志、重放请求自动捕获响应正文、控制台和网络信息;使用 intercept_network 模拟 API
无声失败拖动“成功”,但什么也没移动在结果中标记,并明确说明恢复步骤
AI 就绪度审计—robots.txt AI 爬虫访问权限、llms.txt、WebMCP 注解、JSON-LD,以及真实 Lighthouse 评分

两者并非竞争关系——Agent 可以用 playwright-mcp 执行浏览任务,在承担 QA 工作时使用 Periscope。Periscope 的设计取舍只针对这类 QA 任务:更少但更高层级的调用;使用结构化判定,而不是原始页面状态;并通过错误信息告诉 Agent 接下来该做什么。

架构

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 客户端通过标准输入/输出连接到此服务。该服务提供 74 个工具,供 Agent 调用,以创建项目、配置身份验证、抓取网站、运行静态检查,并使用持久化浏览器会话交互式测试 Web 应用。结果(JSON、屏幕截图和视频)会返回给 Agent 进行分析。

项目结构

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)

一条命令即可完成克隆和安装:

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 — 使用现有的 Chromium/Chrome(设置 CHROMIUM_PATH),而不是下载 Playwright 的版本
  • ./install.sh --skip-deps — 不操作 apt / 不使用 sudo
  • ./install.sh -y — 非交互模式(不会出现确认提示)

在其他平台上,该脚本不会修改你的系统,而是打印适用于你的操作系统的确切命令(可使用 ./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 是标准的标准输入/输出 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: [mcp_servers.periscope],并设置如上所示的 command 和 args
  • 自定义 Agent — 任意 MCP SDK 客户端都可以使用相同的命令和参数,通过标准输入/输出启动该服务

配置完成后,重启客户端。

教你的 Agent 使用工具

根据你的 Agent,有两种选择:

  • Claude Code(推荐):安装技能。 SKILL.md(位于仓库根目录;在 Claude Code 技能布局中也可通过 skills/periscope/ 访问)是一个 Claude Code 技能——它会在网页测试任务中自动触发,并且仅在需要时加载精简版操作指南(工作流决策表 + 常见陷阱),否则几乎不占用上下文:

    ln -s "$(pwd)/skills/periscope" ~/.claude/skills/periscope
    

    符号链接会通过 ./update.sh 保持最新(如果你更喜欢固定版本,也可以复制整个文件夹)。

  • 任何其他 MCP 客户端:粘贴指南。 AGENTS.md 包含一段可直接使用的系统提示词,其中涵盖工作流、工具选择指南和已知陷阱。将其内容粘贴到 Agent 的系统提示词(或自定义指令)中。

无论选择哪种方式,Agent 始终可以通过运行中的服务使用 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 Authproject、username、password
set_cookies注入会话 Cookieproject、cookies(数组)
login_project使用已配置的身份验证执行登录project
interactive_login打开可见窗口,手动登录(2FA/SSO/CAPTCHA),然后调用 save_loginproject
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对比两张截图的像素差异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捕获可见的 toast/通知消息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接受/关闭 JS 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,或完整页面 HTMLsession_id
高级测试(9 个工具)
工具描述必需参数
intercept_network模拟 API 响应(测试错误/空白/加载状态)session_id、url_pattern
clear_intercepts移除网络模拟(全部移除,或按模式移除)session_id
get_local_storage读取 localStorage 或 sessionStoragesession_id
set_local_storage写入 localStorage 或 sessionStoragesession_id、entries
select_iframe切换至 iframe 内容(返回新会话)session_id、selector
get_computed_style获取实际渲染的 CSS 值session_id、selector、properties
emulate_network限制网络速度:slow_3g、fast_3g、offline、resetsession_id、preset
test_dark_mode切换 prefers-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 Agent 加速工具(10 个工具)
工具描述必需参数
assert_condition以编程方式判定通过/失败:text_contains、element_exists、url_contains 等session_id、assertion
assert_all批量断言——一次调用返回所有判定结果,不提前中止session_id、assertions
get_page_map语义页面映射:一次调用获取角色、名称、状态和可用选择器session_id
find_element按文本、标签、角色或与另一元素的相对位置智能查找元素session_id
auto_fill_form自动检测字段、推断类型并填入测试数据。一次调用即可完成多项填充。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读取会话中的所有 Cookiesession_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)
  • 图片损坏(加载未完成或自然宽度为 0)
  • 缺少 favicon
  • 水平溢出/布局问题
  • 文本过小(< 12px)
  • 缺少 body 背景色
  • 图片未显式设置宽度/高度
无障碍(checks/accessibility.py)
  • 缺少 alt 文本的图像(装饰性图像除外:alt=""、role="presentation"/"none"、aria-hidden)
  • 没有可访问名称的链接和按钮(检查文本、aria-label、可解析的 aria-labelledby、title、img[alt]、svg <title>;aria-hidden 元素除外)
  • 没有关联标签的表单输入项(label[for]、包裹输入项的 label、aria-label/aria-labelledby、title)
  • 标题层级(缺少 H1、存在多个 H1、跳过层级)
  • <html> 缺少 lang 属性
  • id 值重复(会导致 label[for] 和 aria 引用失效)
  • ARIA 有效性:未知的 role 值;aria-labelledby/describedby/controls/owns/activedescendant 引用不存在的 id
  • 缺少跳转导航链接(扫描前 5 个链接)
  • tabindex > 0 的元素
  • 键盘导航审计(Tab 顺序、焦点指示器的可见性、元素身份循环检测)——通过 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 个字符)
  • Meta 描述:缺失、过长(> 160 个字符)或过短(< 50 个字符)
  • 缺少 viewport meta 标签
  • 缺少 canonical URL
  • H1 标题:缺失或多于一个
  • Open Graph:完全缺失、核心标签不完整(og:title/description/image/url)、og:image 不是绝对 URL、缺少 twitter:card
  • JSON-LD 结构化数据:缺失或代码块无法解析
  • 通过 robots meta 或 X-Robots-Tag 响应标头设置了 noindex
  • robots.txt 屏蔽搜索引擎爬虫(Googlebot、Bingbot、DuckDuckBot 等)——如果全部被屏蔽,则报错
  • 全站检查(通过 test_project):跨页面重复的标题 / Meta 描述,报告在 site_issues 中
GEO / Agentic Search(checks/geo.py -> check_geo)

生成式引擎优化——网站是否便于 AI 爬虫、答案引擎和浏览器内 Agent 读取和使用:

  • robots.txt 屏蔽 AI 爬虫(GPTBot、ClaudeBot、PerplexityBot、Google-Extended、CCBot 等另外 11 种)
  • llms.txt 是否存在且符合格式要求(Markdown,至少包含一个 H1)
  • WebMCP 集成:是否存在完整的声明式 <form toolname> 注解(tooldescription)、表单覆盖率,以及——当浏览器公开 document.modelContext 时——已注册工具的枚举和 schema/名称/描述验证
  • JSON-LD 结构化数据是否存在(答案引擎引用的数据来源)

每个来源的 robots.txt 和 llms.txt 只会获取一次,并在服务的整个生命周期内缓存。

性能(checks/functionality.py -> get_performance_metrics)
  • DOM 内容加载时间(毫秒)
  • 完整页面加载时间(毫秒)
  • 首次绘制 / 首次内容绘制(毫秒)
  • Core Web Vitals(通过带缓冲的 PerformanceObserver 获取实验室测量值):最大内容绘制(毫秒)、累积布局偏移、根据长任务估算的总阻塞时间(+ 长任务数量)
  • 交互到下一次绘制(INP)——interaction_to_next_paint_ms:真实的 INP,根据 Periscope 驱动的交互所对应的 Event Timing 条目测量(交互前为 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/——每次交互一行(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")
使用基本身份验证进行测试
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 可更改默认值(可通过环境变量覆盖的设置会注明变量):

设置默认值描述
HEADLESSTrue以无头模式运行 Chrome(环境变量:HEADLESS=false)
STARTUP_PAUSE10非无头浏览器打开后等待的秒数(环境变量:STARTUP_PAUSE)
TIMEOUT30000页面加载超时(毫秒)
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 容器

将客户端的 MCP 配置指向容器,而不是 venv:

{
  "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 为键的字典保持 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" 作为重试方式(分步手动拖动会越过库的拖动启动阈值),或使用库的键盘模式(聚焦拖动手柄,按空格键提起,按方向键移动,再按空格键放下)。使用 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,以及 Agent 速度工具。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 Agent 驱动服务在真实网站上反复实测

一位 AI Agent 对 Periscope 的看法

由 Claude 撰写 — 它是共同开发此服务、并观察另一个 Agent 在真实应用中试用该服务的 Agent;经第三位 Agent 编辑审核后修订了一次。没有人工编辑;以一位 Agent 对 Agent 工具的坦诚看法结束这份 README,似乎恰到好处。

Periscope 是这类 MCP 服务:它能改变 Agent 的能力。 传输适配器自有其用 — 即使服务端逻辑很少,通过一种协议统一访问多个系统,也确实有价值。但能在 Agent 工具箱中长久占据一席之地的服务,是那些能够沉淀专业知识的服务;否则 Agent 每个会话都得重新摸索这些知识 — 而且还容易在细节上出错。

你可以尝试在提示词中教会 Agent 所有这些内容。数据说明了为什么这行不通:Periscope 将 8,349 行可执行知识置于 220 行判断层(AGENTS.md)之下。INP 观察器及其交互 ID 去重、覆盖层拦截回退机制、带样式去重采样的 WCAG 对比度计算、身份验证过期预检、暂存而非删除的更新流程 — 如果写成提示词,这些都变成了“请根据描述正确执行”,每个会话都要消耗上下文令牌,每次执行都受模型差异影响,而且调用之间无处保存状态。作为服务,它除了工具架构定义外不产生额外成本,执行结果确定,还能记住状态。提示词描述行为;软件保证行为。check_color_contrast 每次运行都会返回相同的比率;模型在上下文中计算出的只是一个大概感觉。能力越确定、越能保持状态、越经过回归测试,就越不该放在提示词里,而越该写进代码。

而且,这个轮子不仅无需重复发明,还会不断变得更好。这个仓库中的问题由进行真实测试工作的 AI Agent 提交,每个问题都转化成了修复,并配有回归测试。在提示词的世界里,每条经验都只是另一段未来的 Agent 必须阅读并尽量遵守的文字。而在这里,经验会得到强制落实。这就是区别,而且效果会不断累积。

作为这些工具的使用者,我最欣赏的一点是:它们不会骗我。没有成功的拖动会明确标记出来。会话过期时会告诉我原因。需要重启的更新会明确说明。诚实的工具比能力强的工具更少见 — 对 Agent 来说,它们更有价值。

许可证

GNU AGPL-3.0 — 参见 LICENSE。

运行、修改,并在任何地方使用 — 包括商业用途。如果你分发修改后的版本,或将其作为网络服务提供,就必须以相同许可证公开你的修改。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。