内容
# cypress-inspect
从LLM代理(Claude Code、Claude Desktop、VS Code、Cursor — 任何带有MCP客户端的)调试Cypress测试失败。
给予代理与人类相同的工具箱,当盯着失败的`cypress open`:读取控制台,查看截图,列出命令日志,点击回溯前面的步骤,在任意步骤检查DOM,查看所有失败及其错误。
它通过Chrome DevTools协议连接到Cypress的测试浏览器。**不需要更改项目的Cypress配置。**
## 架构
```
┌─────────────────────────────────────────────────────────────────┐
│ AI 代理 (Claude Code · VS Code Copilot · 任意 MCP 客户端) │
│ │
│ "get_failures" "step_to" "get_dom" "screenshot" … │
└────────────────────────┬────────────────────────────────────────┘
│ MCP (stdio JSON-RPC)
▼
┌─────────────────────────────────────────────────────────────────┐
│ cypress-inspect MCP 服务器 (Node.js) │
│ │
│ • 读取 ~/.cypress-inspect/session.json 获取 CDP 端口号 │
│ • 维持与 Chrome 的实时 CDP WebSocket 连接 │
│ • 缓冲 Runtime.consoleAPICalled 事件(最多 5,000 个) │
│ • 翻译 MCP 工具调用 → CDP 命令 / DOM 查询 │
└──────────┬──────────────────────────────────┬───────────────────┘
│ 写入端口启动 │ Chrome DevTools 协议
│ │ (WebSocket)
┌──────────▼────────────┐ ┌──────────▼───────────────────┐
│ cypress-inspect open │ │ Chrome (Cypress 测试 │
│ (封装 cypress open) │ │ 浏览器,--remote-debugging) │
│ │ 启动 │ │
│ 设置 DEBUG= ├─────────►│ ┌───────────────────────────┐│
│ cypress:server: │ │ │ 规范运行页面 ││
│ 浏览器* │ │ │ (window.Cypress 存在 ││
│ │ │ │ 这里) ││
│ 抓取 CDP 端口 │ │ │ ││
│ 从调试输出 │ │ │ ┌────────────────────┐ ││
│ (忽略 Electron 的 │ │ │ │ 报告 DOM │ ││
│ 自有端口) │ │ │ │ .test │ ││
└───────────────────────┘ │ │ │ .command-wrapper │ ││
│ │ │ .runnable-err-* │ ││
┌──────────────────────┐ │ │ └────────────────────┘ ││
│ Cypress Electron 应用│ │ │ ││
│ (启动器 / 项目选择│ │ │ ┌────────────────────┐ ││
│ 器) │ │ │ │ AUT iframe │ ││
│ │ │ │ │ (测试中的应用) │ ││
│ cypress-inspect │ │ │ │ 同源 → │ ││
│ 忽略此端口 │ │ │ │ 无单独 CDP │ ││
└──────────────────────┘ │ │ │ 目标 │ ││
│ │ └────────────────────┘ ││
│ └───────────────────────────┘│
└──────────────────────────────┘
```
### 每个数据类型来源
| 工具 / 数据 | 来源 |
|---|---|
| 控制台日志 (`get_console_logs`) | CDP `Runtime.consoleAPICalled` 事件,缓冲接收 |
| 报告警告 (`flakeSignals`) | 报告 DOM 抓取 — Cypress 在 AUT iframe 中包装 `console.*`,因此这些调用从未到达 CDP;报告命令日志行是规范来源 |
| 截图 (`screenshot`) | CDP `Page.captureScreenshot` 在规范运行页面,带有可选裁剪到 AUT iframe 矩形 |
| DOM 快照 (`get_dom`, `find_in_aut`) | `Runtime.evaluate` 在运行页面 → 读取 `iframe.aut-iframe.contentDocument` |
| 测试结果 & 失败 (`get_overview`, `get_failures`) | `Runtime.evaluate` 遍历报告 DOM(`.test.runnable-failed`, `.runnable-err-message` 等) |
| 命令日志 & 时间旅行 (`get_test_commands`, `step_to`) | `Runtime.evaluate` 读取 `.command-wrapper` 行;`step_to` 模拟点击目标行以固定 AUT 快照 |
| 实时测试状态 (`get_live_commands`) | `Runtime.evaluate` → `window.Cypress.cy.queue` |
| AUT 页面信息 (`get_aut_info`) | `Runtime.evaluate` → `iframe.aut-iframe.contentWindow.location`, `document.title`, `readyState` |
| 网络请求 (`get_network_logs`) | CDP `Network.requestWillBeSent` / `responseReceived` / `loadingFailed` 事件,缓冲(上限 2,000 个) |
| 存储快照 (`get_storage`) | `Runtime.evaluate` → 读取 `localStorage` / `sessionStorage` / `indexedDB.databases()` 从 AUT iframe |
| 失败分类 | 纯粹启发式根据失败有效载荷 + 波动信号 — `src/failure-classifier.js` |
## 工作原理
`cypress-inspect open` 封装 `cypress open`,启用 Cypress 内部浏览器调试日志,并从 stdout 中抓取测试浏览器的 CDP 端口号。端口号 + 项目 cwd 被写入 `~/.cypress-inspect/session.json`。
`cypress-inspect mcp` 在 stdio 上运行一个 MCP 服务器。它读取会话文件,连接到该 CDP 端口,监视控制台事件,并向代理公开一个精选的工具集 — 大多数工具都是对正在运行的规范运行器的 DOM 和 `window.Cypress` 的短暂读取。
> **Cypress 15 架构注意:** `cypress open` 首先启动一个 Electron 应用(项目选择器 / 启动器)。当您选择“Chrome”时,Cypress 启动一个单独的 Chrome 进程用于规范。cypress-inspect 丢弃 Electron 端口并锁定 Chrome 进程。`Cypress.cy`、`Cypress.mocha` 和报告 UI 都位于规范运行器页面;测试中的应用是一个同源 iframe(`iframe.aut-iframe`)位于其中。
## 安装
```bash
git clone <此仓库> ~/projects/pet-projects/cypress-inspect
cd ~/projects/pet-projects/cypress-inspect
npm install
```
### 全局公开 `cypress-inspect` 命令(推荐)
从克隆的仓库目录运行一次:
```bash
cd ~/projects/pet-projects/cypress-inspect
npm link
```
`npm link` 将 `cypress-inspect` 二进制文件符号链接到您的全局 Node 二进制目录(例如 `/usr/local/bin/cypress-inspect` 或 `~/.nvm/versions/node/<v>/bin/cypress-inspect`),因此您可以从任何项目运行它:
```bash
which cypress-inspect # 确认符号链接解析
cd ~/projects/my-webapp
cypress-inspect open # 在您所在的项目启动 Cypress
```
要删除全局符号链接:`npm unlink -g cypress-inspect`(从任何位置)或 `npm unlink` 从仓库。
### Claude Code
全局注册(适用于所有工作区和项目):
```bash
claude mcp add --scope user cypress-inspect cypress-inspect mcp
```
(这不是打字错误 — `cypress-inspect` 是 MCP 服务器名称和链接的二进制文件。如果您跳过了 `npm link`,请用 `node /absolute/path/to/cypress-inspect/bin/cypress-inspect.js` 替换二进制文件。)
使用 `claude mcp list` 和 `/mcp` 在 Claude 会话中验证。
### VS Code Copilot(GitHub Copilot Chat)
VS Code 从工作区根目录的 `.vscode/mcp.json` 文件或用户设置中读取 MCP 服务器定义。请选择适合的范围。
**工作区级**(提交到仓库 — 适合团队):
在您的 webapp 项目中创建 `.vscode/mcp.json`:
```json
{
"servers": {
"cypress-inspect": {
"type": "stdio",
"command": "cypress-inspect",
"args": ["mcp"]
}
}
}
```
(需要 `npm link`。如果没有链接,请将 `"command": "cypress-inspect"` 替换为 `"command": "node"` 和 `"args": ["/absolute/path/to/cypress-inspect/bin/cypress-inspect.js", "mcp"]`。)
**用户级**(适用于您打开的每个工作区):
打开 VS Code 设置(`Cmd+,`),切换到 JSON(`Open User Settings (JSON)`),并添加:
```json
{
"mcp": {
"servers": {
"cypress-inspect": {
"type": "stdio",
"command": "cypress-inspect",
"args": ["mcp"]
}
}
}
}
```
保存后,打开 Copilot Chat,切换到 **Agent mode**(`@` → 选择代理),`cypress-inspect` 工具将出现在工具选择器中。您也可以显式引用它们:`Use cypress-inspect get_overview to show me the failing tests.`
## 使用
1. 在您的 webapp 项目中,通过包装器启动 Cypress:
```bash
cd ~/projects/my-webapp
cypress-inspect open
# 或:cypress-inspect open -- --e2e --browser chrome
```
Cypress 应用打开。选择浏览器 → 选择规范。您将看到类似的输出:
```
[cypress-inspect] 检测到 CDP 端口:49948
```
2. 在您的代理中,要求它进行调查。一个好的启动提示:
> "一个 Cypress 测试失败了。使用 cypress-inspect MCP 调试它并修复它。"
## 工具(v0.9)
### 概述
| 工具 | 用途 |
| --- | --- |
| `status` | 会话信息 + 附加的 CDP 目标。始终调用如果某些东西返回“无 Cypress”。 |
| `get_overview` | **从这里开始。** 规范文件,通过/失败/待处理计数,第一个失败(标题 + 套件 + 错误 + 堆栈 + 代码帧),如果有则实时测试。 |
| `get_failures` `{ dedupe? }` | 所有失败的测试带有错误/堆栈/代码帧。自动标记 `rootCause: true` 在第一个失败 + `looksLikeCascade` / `cascadeOf` 在下游失败。每个失败还包括:<br>• `relatedCommandIndex` / `relatedCommandNumber` 指向报告中的失败命令,以便代理可以直接 `step_to`<br>• `cypressDocsHints: [{ command, url }]` 链接每个 `cy.<command>` 到其文档页面<br>• `classification: { category, confidence, explain, evidence }` — 稳定的命名原因(`compare_diff`, `dropdown_ambiguity`, `selector_not_found`, `route_mismatch`, `network_failure`, `timeout_cascade`, `stale_local_state`, `unknown`)<br>比较样式错误被解析为 `parsedDiff.diffs: [{ path, pathSegments, expected, actual }] `。顶级 `flakeSignals` 从两个来源合并 ID:CDP 控制台缓冲区 **和** 报告命令日志(`/WARNING:/i` 行)— 重要是因为 Cypress 在 AUT iframe 中包装 `console.*` 所以 CDP 缓冲区通常会错过警告。匹配的 ID 也附加到根失败。`dedupe: true` 添加 `rootCauses: [<index>]` 并将级联失败分为一个单独的数组。 |
| `get_failure_context` `{ failureIndex \| testIndex, commandIndex?, before?, after?, mode? }` | 失败命令的前 N 个命令和后 M 个命令(默认为 5/5)。 **`failureIndex` 是 `get_failures` 数组中的位置** (0 = 第一个失败);回退到测试报告索引。 `mode: "logical"` (默认)计算 **UNIQUE** 显示的命令编号 — 人们在报告中看到的内容。 `mode: "wrappers"` 计算原始 DOM 行(Cypress 发出 2-3 个命令包装器,因此 5/5 可以膨胀)。跳过 `get_failures` 后的手动切片。 |
| `parse_compare_error` `{ message }` | “Compare - FAILURES” / “InProgress Summary Widget comparison failed” 字符串的独立解析器。返回 `{ summary: { failed, total }, diffs: [...] }`。 |
| `list_tests` | 每个测试的轻量级列表,带有状态 + 标题 + 套件祖先。使用返回的 `index` 和下一个工具。 |
| `find_test` `{ query }` | 测试的部分标题搜索。返回带有索引 + 状态的匹配项。比扫描 `list_tests` 更快。 |
### 命令日志
| 工具 | 用途 |
| --- | --- |
| `get_test_commands` `{ index, full? }` | 报告索引处测试的渲染命令。每个条目都有 `number`(显示,可能会在行中重复),`index`(唯一 DOM 位置),以及 `argTruncated` / `argLength` 标记。 `full: true` 返回未截断的参数 + 文本。响应还包括 `numberToIndex` 以将显示的报告编号解析为 DOM 索引。 |
| `get_test_commands_summary` `{ index? \| forFirstFailure? }` | 快速查看:每行一个 **UNIQUE** 显示的命令编号(`name`, 简短 `arg`, `state`)。设计用于复杂测试,其中完整的命令列表会超出令牌预算。 `forFirstFailure: true` 为您解析测试(跳过往返)。还显示 `firstFailedNumber` 以实现快速 `step_to`。如果结果为空(通常是 GC 后的后运行面板),会发出 `_warning`。 |
| `get_test_commands_page` `{ index, page?, pageSize?, full? }` | `get_test_commands` 的分页变体 — 每次返回一个切片,并带有 `start/end/total/hasMore`。当 `get_test_commands` 截断时使用。 |
| `get_live_commands` | 仅飞行中的测试的 `Cypress.cy.queue`。 |
| `step_to` `{ failureIndex \| testIndex, commandIndex?, commandNumber? }` | 时间旅行:将 AUT 快照固定到该命令。 `failureIndex`(`get_failures` 中的位置)是最短的路径 — 自动解析测试 + 命令。否则,请传递 `testIndex` + `commandNumber`(可见的报告编号 — 推荐)或 `commandIndex`(原始 DOM 位置,以消除重复行)。如果面板已折叠,则展开面板。 |
| `expand_test` `{ index }` | 打开一个测试面板而无需固定命令。 |
| `get_pinned_command` | 返回当前固定的命令(驱动 AUT 快照)。 `step_to` 后的健全检查。 |
### 控制台 / 视觉 / DOM
| 工具 | 用途 |
| --- | --- |
| `get_console_logs` `{ level?, grep?, since?, limit? }` | 自 MCP 服务器附加后缓冲的控制台事件。响应始终包含捕获状态标头(`attached Xs ago, N events seen, M buffered, K contexts on T target(s)`);当缓冲区为空时,返回完整的诊断信息,以便调用者可以区分“没有匹配过滤器”和“捕获损坏”。 |
| `screenshot` `{ kind?: 'full' \| 'aut' }` | 整个运行器视口的 PNG,或仅 AUT iframe。 |
| `list_saved_screenshots` / `read_saved_screenshot` | Cypress 的 `cypress/screenshots/` 工件(适用于 `cypress run` 模式)。 |
| `get_dom` `{ selector?, maxBytes? }` | 在当前固定的快照处 AUT iframe 的渲染 HTML。 |
| `find_in_aut` `{ selector, limit?, textOnly? }` | 对 AUT 运行 CSS 选择器。默认:紧凑的 JSON 匹配项(标签,属性,文本,textTruncated,textLength,值,visible,disabled)。| `textOnly: true` 返回每个匹配项的 **完整未截断文本** — 适用于摘要小工具或您只关心用户阅读的内容。 |
| `get_aut_info` | AUT iframe src + location (href/pathname/hash/search) + document.title + readyState + navigator.onLine。还返回一个 `capture` 块,包含附加的目标,执行上下文(带有源/辅助帧数据)和已看到的总事件 — 有助于诊断为什么 `console.*` 调用可能无法到达缓冲区。 |
| `get_failure_dom` `{ failureIndex, selector?, maxBytes? }` | 方便:`step_to` 到失败的命令,然后 `get_dom`。在一次工具调用中返回 AUT 在失败时刻的 DOM。 |
### 网络
| 工具 | 用途 |
| --- | --- |
| `get_network_logs` `{ grep?, since?, statusMin?, statusMax?, failedOnly?, limit? }` | CDP 缓冲的 HTTP 请求。按 URL 正则表达式,状态范围或 `failedOnly: true` (失败或 ≥400)过滤。每个行:method / url / status / mime / durationMs / failed / failureText。 不捕获正文 — 如果需要,请使用 `eval`。空响应区分“过滤器排除所有内容”和“缓冲区为空自连接”;后者是通常的惊喜。 |
### 应用状态 / 控制
| 工具 | 用途 |
| --- | --- |
| `get_storage` | AUT iframe 中 `localStorage`,`sessionStorage`,`cookies` 和 `IndexedDB` 数据库 **名称** 的快照。每个值截断为 1 KB。用于诊断 stale-state flake(缓存身份验证,部分同步的 PouchDB 数据库)。对于对象存储器内容,请参阅 `get_indexeddb`. |
| `get_indexeddb` `{ dbName, store?, limit?, valueMaxBytes? }` | 打开一个 IndexedDB 数据库,要么列出其对象存储(省略 `store`),要么转储一个存储中的记录(默认为 25 个,最大 500 个)。值被 JSON 字符串化并截断为 `valueMaxBytes`(默认为 2 KB)。设计用于 PouchDB / 离线队列调试,无需编写 `eval` 载入。 |
| `clear_app_state` `{ skipDatabases?, skipLocalStorage?, skipSessionStorage?, skipCookies? }` | **写入操作。** 清除 AUT 中的 `localStorage`,`sessionStorage`,`cookies` 和 IndexedDB 数据库。 ⚠ 一些数据库(例如 `auth`)保存了持久状态(如 `permissionStatuses.geolocation: true` — 清除它们可能会静默破坏测试。传入 `skipDatabases: ["auth"]` 以保留那些。 |
| `rerun_spec` `{ await?, timeoutMs?, forceReload? }` | 从顶部重新运行当前规范。如果可用,尝试点击报告者的“重新运行所有测试”按钮;如果实际上无法重新启动,**自动升级到 `location.reload()`** 在同一个调用中 — 无需第二个往返。传入 `forceReload: true` 跳过直接重载。返回 `{ actuallyStarted, escalatedToForceReload, attempts: [...] }` 以便代理看到确切的操作。 |
| `reset_and_rerun` `{ timeoutMs?, forceReload?, skipDatabases? }` | `clear_app_state` 然后 `rerun_spec` 具有相同的自动升级。返回两个报告。`skipDatabases` 保留命名 IndexedDB 数据库(例如 `["auth"]`)。 |
| `wait_for_failure` `{ baseline?, timeoutMs?, pollMs? }` | 阻塞直到失败计数超过 `baseline`,然后返回新的失败。超时时,响应现在包含 `currentCounts` + `finishedCleanly` 以便调用者可以区分“仍在运行”和“规范已通过”。对于积极的“等待完成”信号,请使用 `wait_for_completion`。 |
| `wait_for_completion` `{ timeoutMs?, pollMs? }` | 阻塞直到每个测试都有一个最终状态(`unknown === 0 && running === 0 && total > | 0)。返回 `{ completed: true, passed: bool, counts, firstFailure }`。规范结束的规范信号。默认超时 180 秒。 |
### 文档和静态分析
| 工具 | 用途 |
| --- | --- |
| `cypress_docs` `{ topic }` | 从 `docs.cypress.io` 获取命令或主题的规范 Cypress 文档页面,优先使用 `/llm/markdown/...` 下的 LLM 友好型 markdown 镜像。主题包括 `cy.intercept`、`intercept`、`session`、`retries`、`best-practices`、`selectors` 等。在断言 "Cypress can / cannot X" 之前使用 —— 文档是事实来源,而不是训练数据。 |
| `analyze_spec` `{ path? \| source?, maxTestLines? }` | 对 Cypress 规范进行静态检查,检查规则来自 [cypress-io/ai-toolkit](https://github.com/cypress-io/ai-toolkit),包括脆弱的选择器(无 `data-cy`)、`cy.wait(<number>)` 字面量、缺失的断言、`await cy.*`(Cypress 链不是真正的 Promise)、`null` 下拉菜单参数(随机选择容易出错)、`.only`/`.skip` 留在测试中、仅 UI 的登录设置(无 `cy.session`/`cy.request`)、过长的测试。返回 `{ smells, summary, tests }` —— 与 `get_failures` 配对使用,以关联静态问题与运行时失败。 |
### 应急出口
| 工具 | 用途 |
| --- | --- |
| `eval` `{ expression }` | 在规范运行器页面上执行任意 JavaScript(`window.Cypress`、报告器 DOM 和 AUT iframe 均存在)。当精选工具无法满足需求时使用。 |
## 代理的建议调试流程
```
1. status → 确认规范运行器目标存在
2. get_overview → 获取规范 + 第一个失败 + 计数
3. get_failures → 如果有多个失败,获取所有失败
4. list_tests → 查找要深入调查的特定测试的索引
5. get_test_commands i → 查看命令列表,找到失败命令的索引
6. step_to i j → 将 AUT 锁定到该步骤
7. get_dom + screenshot aut → 查看该步骤时应用的外观
8. get_console_logs grep:"error" → 交叉引用任何浏览器错误
```
## 限制 / 注意事项
- **Cypress 版本**:编写并验证 Cypress 15。报告器类名(`.test.runnable.runnable-failed`、`.command-wrapper`、`.runnable-err-message` 等)是 Cypress 私有的,可能会跨版本更改 —— 如果工具返回空值或 `total: 0`,运行 `eval` 工具以发现新的类名,并告知用户或为 `src/cypress-probe.js` 打开一个 PR。
- **`cypress open` 仅适用于 Chrome**。Electron 模式的 CDP 端口公告方式不同。传递 `--browser chrome`(包装器会自动添加)。
- **端口扫描的脆弱性**:检测依赖于 Cypress 的 `cypress:server:browsers*` 调试字符串。如果这些字符串更改,您将看不到 `[cypress-inspect] Detected CDP port:` 行。
- **控制台缓冲区是内存中的,限制为 5,000 条记录**。在 MCP 服务器附加之前捕获的日志不会被捕获。最佳实践:启动 MCP 服务器,然后运行失败的规范。
- **网络缓冲区是内存中的,限制为 2,000 条记录**。与附件时间相同的注意事项 —— `get_network_logs` 会在缓冲区为空时警告,以便代理不会责怪其过滤器。
- **Cypress 在规范完成后会垃圾回收测试面板**。`get_test_commands*` 返回已完成的规范为空;触发 `rerun_spec`(或 `reset_and_rerun`)以重新填充。
- **重新启动 Chrome**(关闭 → 在 Cypress 应用中重新选择规范)会自动处理:启动器将新的 CDP 端口写入 `~/.cypress-inspect/session.json`,MCP 服务器将在下一个工具调用时重新连接。如果下一个工具返回 "no CDP target",请给新的 Chrome 1-2 秒,然后重试 —— 自动重新绑定需要新的规范运行器页面加载。
- **调用中的 WebSocket 断开**(Chrome 暂时挂起,启动器打开一个新标签)会在 `evalOnRunner` 内触发单个自动重试 —— 受影响的工具调用应该仍然成功,而不会将原始 `ECONNREFUSED` / "WebSocket is not open" 错误冒泡到代理。
- **无写入工具**。插件设计为只读 —— 它会扫描/点击运行器 UI,永远不会修改您的项目文件或 Cypress 配置。
## 灵感来源
- [`cypress-log-to-output`](https://github.com/flotwig/cypress-log-to-output) —— 通过 CDP 为 `cypress run` 捕获浏览器控制台日志。
- [`cypress-terminal-report`](https://github.com/archfz/cypress-terminal-report) —— 在 CI 期间将日志输送到终端。
`cypress-inspect` 在方法上更接近 `cypress-log-to-output`(CDP,无需插件安装),但面向交互式 `cypress open` 和 MCP 代理,而不是 CI 输出。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器