React应用调试工具

一个专注于运行时调试React应用程序的MCP服务器,提供组件状态检查、Redux/Zustand存储查看、API响应体捕获等功能。

  • by andinh2706
  • TypeScript
  • 本地部署
  • 更新于
在 GitHub 查看 andinh2706/fe-mcp

使用说明

反应调试mcp

一个专注的MCP服务器 React应用程序的运行时调试设计用于并排运行 chrome开发工具mcp.

问题 服务器
导航、单击、填写表单、截图 chrome devtools mcp
控制台日志、性能跟踪 chrome devtools mcp
React组件状态和道具 反应调试mcp
Redux/状态存储检查 反应调试mcp
API响应机构 反应调试mcp
断点、步进、范围检查 反应调试mcp
日志点(非暂停值捕获) 反应调试mcp

工具(共26个)

反应检查

工具 目的
get_page_info 检测React、版本、存储、dev/prod、DevTools挂钩
get_component_tree 带有名称、键、可选挂钩/道具的组件层次结构。支持 start_selector 扩展到子树
get_react_component_path 从根组件到CSS选择器处组件的路径。每个节点可选的道具/钩子
find_react_component(name) 使用道具过滤按名称查找所有实例。支持 start_selector 以确定搜索范围。返回完整 parentPath 从根
inspect_react_component_by_name(name) 按名称+目标深度检查特定实例
inspect_react_component(selector) 通过CSS选择器进行检查(快速路径)
inspect_react_context(selector) 所有上下文。流入组件的提供程序值
get_react_error_boundaries 错误边界、暂停回退、开发覆盖

部件检查工具-- get_component_tree, get_react_component_path, find_react_component, inspect_react_component,以及 inspect_react_component_by_name --接受 show_function_details 将函数值扩展为 {functionName, functionBody, paramCount} 而不是 "[function]".

状态与网络

工具 目的
get_store_state(path?) 使用点路径读取Redux/Zustand状态
get_network_responses(url_pattern?) API请求-实时请求包括全文,历史请求(在MCP连接之前)包括URL/状态/时间

看到 forwardRef(Anonymous) 在组件树中? 该名称在运行时确实不存在,也不是此服务器中的错误(React DevTools也显示了同样的情况)。 forwardRef() 返回异国情调 物体,以及一个写 export const MdcButton = forwardRef((props, ref) => …) 既不设置 displayName 也不 render.name (JS推断一个函数的 .name 仅在赋值时,从不在调用参数位置),因此React没有可报告的名称。通过将导出名称复制到 displayName 在dev中:

import * as Ids from 'ids-wc/dist/react/components';
import { nameLibraryComponents } from './react-debug-helper';

nameLibraryComponents(Ids);   // forwardRef(Anonymous) → MdcButton, MdcGrid, …

get_store_state 是另一个需要你的应用程序配合的工具:store是一个没有DOM存在的闭包变量,所以只有当你把它放在 window 全球(__REDUX_STORE__, __ZUSTAND_STORE__,或 __STORE__).要么自己设置全局,要么 复制 react-debug-helper.ts 进入应用程序的源代码树并调用 exposeStore(store, 'redux' | 'zustand') 你创建商店的地方——它没有开发之外的操作。无论你揭露什么,都必须有一个 .getState() 方法(对于Zustand来说,这意味着钩子本身,而不是 useStore()).没有这个, get_store_state 报告 { store: 'none' }.

源代码检查(捆绑脚本,无暂停)

工具 目的
list_scripts(filter?) 发现加载的捆绑脚本 set_breakpoint 匹配不明确
read_source(file) 从浏览器读取捆绑/生成的源代码——查看供应商代码或确认加载的内容
search_source(query) 搜索加载的捆绑脚本,以缩小从磁盘读取的文件范围

断点(交互式,暂停执行)

工具 目的
set_breakpoint(file, line) 设置断点——首先从磁盘读取源文件以选择行
wait_for_breakpoint 阻塞直到断点触发,返回作用域
inspect_scope(frame_index?) 读取任何堆栈帧上的变量
evaluate_at_breakpoint(expression) Eval在暂停帧的上下文中
step_over / step_into / step_out 执行控制
resume 继续执行

日志点(被动,无暂停)

工具 目的
set_logpoint(file, line, expressions) 在不暂停的情况下将值捕获到控制台

管理

工具 目的
list_breakpoints 显示所有活动断点和日志点
remove_breakpoint(id) 删除一个
remove_all_breakpoints 干净的石板
evaluate_in_page(expression) 临时JS评估逃生舱

调试工作流

只要您的开发服务器发出源代码,磁盘上的行号就与浏览器匹配 地图(webpack devtool: source-mapVite等)。从文件系统读取文件 选择断点行,然后通过编辑相同的文件来修复错误。

断点流

You: "The checkout total is wrong. Debug it."

Agent: (reads src/hooks/useCheckout.ts from filesystem)
  → 40 │ function calculateTotal(items) {
     41 │   const subtotal = items.reduce((s, i) => s + i.price, 0);
     42 │   const taxRate = getTaxRate();
     43 │   const tax = subtotal * taxRate;
     44 │   return { subtotal, tax, total: subtotal + tax };

Agent: set_breakpoint({ file: "useCheckout.ts", line: 42 })
  → "Breakpoint set. Have user trigger the code path."

Agent: "Please click the checkout button."

You: (click checkout in the browser)

Agent: wait_for_breakpoint()
  → paused at useCheckout.ts:42
     scope: { items: [...], subtotal: 29.97, taxRate: undefined }

Agent: "Found the bug — getTaxRate() returns undefined at line 42."

Agent: resume()

Agent: (edits src/hooks/useCheckout.ts on disk to fix the bug)
  → webpack dev server hot-reloads the fix

多断点调查(使用search_source缩小范围):

Agent: search_source({ query: "item.price" })
  → CartItem.tsx:23, PriceDisplay.tsx:8, calculateTotal.ts:12

Agent: (reads src/components/CartItem.tsx from filesystem around line 23)
Agent: set_breakpoint({ file: "CartItem.tsx", line: 23 })
Agent: "Click an item to add to cart."
You: (click)
Agent: wait_for_breakpoint()
  → scope: { item: { id: 5, price: "9.99" } }    ← price is a string!

Agent: (reads src/utils/calculateTotal.ts from filesystem around line 12)
Agent: set_breakpoint({ file: "calculateTotal.ts", line: 12 })
Agent: resume()
Agent: wait_for_breakpoint()
  → scope: { items: [...], sum: "09.999.99" }     ← string concatenation!

Agent: "Root cause: price comes from API as string, not number."
Agent: (edits src/utils/calculateTotal.ts to add parseFloat)

日志点流

You: "Some cart items show wrong prices but I'm not sure which ones."

Agent: (reads src/components/CartItem.tsx from filesystem)
  → line 15: const displayPrice = formatPrice(props.price);

Agent: set_logpoint({
  file: "CartItem.tsx",
  line: 15,
  expressions: { "id": "props.id", "price": "props.price", "computed": "displayPrice" },
  label: "cart-price"
})
  → "Logpoint set. Please browse through the cart items."

Agent: "Please scroll through your cart so all items render."

You: (scroll through)

Agent: (uses chrome-devtools-mcp) list_console_messages
  → Finds messages with ⚡RDM|cart-price prefix:
    ⚡RDM|cart-price|1711234567|{"id":1,"price":9.99,"computed":"$9.99"}
    ⚡RDM|cart-price|1711234568|{"id":2,"price":"5.00","computed":"$NaN"}
    ⚡RDM|cart-price|1711234569|{"id":3,"price":12.50,"computed":"$12.50"}

Agent: "Item 2 has price as string '5.00' instead of number."
Agent: (edits the API response handler on disk to fix the type)

设置

要求节点≥20.18 --运行脚本使用 node --env-file-if-exists 和 --import tsx (建议使用节点22+)。

cd react-debug-mcp
yarn install

服务器是用TypeScript编写的,直接通过 tsx --没有构建步骤,没有 dist/.全部四个 start* 脚本加载 .env 自动(参见 环境变量).

脚本 运行
yarn start 正常启动
yarn start:debug 从打开节点检查器开始 :9229 (附加调试器;断点映射到 .ts)
yarn start:inspect 开始包装在MCP Inspector web UI中
yarn start:inspect:debug MCP检查员 和 节点检查器打开 :9229
yarn typecheck tsc --noEmit (仅进行类型检查;不发出任何信号)

将其作为命令安装(建议用于代理配置)

因此,您的代理的MCP配置不需要绝对路径 src/index.ts,安装 包,因此它暴露了一个 react-debug-mcp 二元的:

npm i -g .        # or: npm link

现在 react-debug-mcp 在你的 PATH 并且可以从任何地方发射。这个 bin/cli.mjs 垫片寄存器 tsx的加载器正在进程中,并导入TypeScript入口点,所以这 仍然需要 没有构建步骤,也没有 dist/ --它的代码路径与 yarn start.

二进制文件确实如此 不 读 .env (与 start* 脚本)。全局命令在中运行 您的应用程序 目录,其中a .env 属于您的应用程序,而不是此服务器。传递配置 通过MCP客户端 environment 取而代之的是块。

OpenCode配置

添加到您的项目 opencode.jsonc:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "react-debug": {
      "type": "local",
      "command": ["react-debug-mcp"],
      "environment": {
        "CDP_PORT": "9222",
        "CDP_TARGET_URL": "localhost:3000"
      },
      "enabled": true
    },
    "chrome-devtools": {
      "type": "local",
      "command": ["npx", "-y", "chrome-devtools-mcp@latest", "--browser-url", "http://127.0.0.1:9222"],
      "enabled": true
    }
  }
}

如果跳过全局安装,回退是显式路径——它有效,但你有 在每台机器上保持正确:

"command": ["npx", "tsx", "/path/to/react-debug-mcp/src/index.ts"]

启动OpenCode时运行时会发生什么

1. You run `opencode` in your project directory.

2. OpenCode reads opencode.jsonc, finds the "react-debug" MCP config.

3. OpenCode spawns:  react-debug-mcp
   with CDP_PORT=9222 and CDP_TARGET_URL=localhost:3000 in the env.
   The process's stdin/stdout become the JSON-RPC transport.

4. MCP handshake (over stdin/stdout):
   ← Client sends:   { method: "initialize", params: { capabilities: { ... } } }
   → Server responds: { capabilities: { tools: {}, logging: {} } }
   ← Client sends:    { method: "notifications/initialized" }

   The server advertises `logging: {}` — this tells the client it
   supports the `logging/setLevel` request and will send
   `notifications/message` log events.

5. Client discovers tools:
   ← Client sends:   { method: "tools/list" }
   → Server responds: [ { name: "get_component_tree", ... }, ... ]  (26 tools)

   OpenCode now includes these tools in the LLM's tool list.

6. Server connects to Chrome eagerly in the background (non-blocking).
   If Chrome isn't ready yet, retries on first tool call.

7. You ask the agent to debug something. The LLM picks a tool:
   ← Client sends:   { method: "tools/call", params: { name: "get_page_info" } }

   → Server evaluates the snippet in the browser, returns result.
   (If eager connect failed, this tool call triggers the retry.)

8. Throughout, the server sends log events. `data` is a structured object
   (not a flattened string), so clients render it as expandable JSON:
   → { method: "notifications/message",
       params: { level: "info", logger: "react-debug-mcp",
                 data: { msg: "connected", pageUrl: "http://localhost:3000" } } }

   These appear in OpenCode's log output and in the MCP Inspector
   logging panel (if running under the Inspector).

MCP检查员

这个 MCP检查员 是web UI 用于手动调用工具并查看原始JSON-RPC交换和日志 实时通知——无需代理。这个 start:inspect 脚本包装 其中的服务器:

yarn start:inspect         # Inspector UI (config from .env)
yarn start:inspect:debug   # Inspector UI + Node inspector on :9229 (breakpoints map to .ts)

两者都在以下位置打开UI http://localhost:6274。在不编辑的情况下覆盖配置 .env,通过 -e KEY=value 在命令之前:

npx @modelcontextprotocol/inspector -e CDP_TARGET_URL=localhost:3000 \
  node --env-file-if-exists=.env --import tsx src/index.ts

⚠️ 检查员无法观察您的代理人的会话

一个自然的假设是: “将检查员指向我的代理人已经在的服务器 与代理人交谈,并观察代理人的行为。" 那是不可能的 --还没结束 stdio,也不能通过HTTP。不要围绕它构建工作流程。

MCP没有观察员角色。 这是点对点JSON-RPC:客户端只能看到 自己的交通。根据设计,没有订阅另一个会话机制的工具 参数和结果通常携带凭据、源和用户数据,因此 默认情况下,跨会话通道将是启用的渗透通道。

运输 为什么它仍然不起作用
标准 (此服务器) 客户端 生成 服务器并拥有其stdin/stdout。你的代理人出生了 过程A;检查员产生了它的 自身流程B两个客户端=两个独立的服务器进程。没有可连接的监听插座
超文本传输协议 (假设) 转换传输确实 不 修好它。检查员会打开它 自己的会话 并且仍然只呈现其自己的请求/响应流量。服务器最多只能 广播 活动作为日志通知——日志窗格中的文本提要,而不是作为结构化请求/响应的代理调用。

所以检查员是为了 动手探究 --你自己开车,在一个 单独的会话,针对同一Chrome。这真的很有用,这就是 start:inspect 是为了。这不是了解代理人的窗口。

停止间隙: 看看什么 代理 确实,使用服务器自己的日志。服务器 是代理呼叫可见的地方,因此观察发生在那里-- 设置 LOG_TOOL_RESULTS=1 和 LOG_FILE,然后跟踪文件。你得到了每一个工具 代理调用、其参数以及返回的内容。看 观看代理会话 在......下面

日志记录

服务器使用双模日志记录:

频道 何时 你看到了什么
MCP通知 服务器连接到客户端后 Inspector日志面板或OpenCode日志输出中的结构化日志条目。客户通过以下方式控制级别 logging/setLevel.
标准错误 始终(包括连接前) JSON行。液位控制 LOG_LEVEL 有人。
日志文件 何时 LOG_FILE 设置 与stderr相同,写入指定的文件路径。

启动消息(在MCP传输连接之前)只会发送到stderr。 一旦连接,每个日志调用都会同时进入两个通道。

观看代理会话

工具总是记录它们的 调用 (名称+参数)。集 LOG_TOOL_RESULTS=1 他们还记录了他们的 结果 一 tool done 带有持续时间、成功标志的行, 结果大小和结果预览(tool threw 如果处理者投掷)。与 LOG_FILE,这使日志成为代理所做工作的完整跟踪 然后回来了:

LOG_FILE=./agent.log LOG_TOOL_RESULTS=1 yarn start   # then, in another terminal:
tail -f agent.log
{"level":"info","msg":"tool done","data":{"tool":"list_breakpoints","ms":1,"ok":true,"chars":35,"preview":"No active breakpoints or logpoints."}}
{"level":"info","msg":"tool done","data":{"tool":"get_page_info","ms":2,"ok":false,"chars":143,"preview":"Error: Cannot connect to Chrome on 127.0.0.1:1 …"}}

结果记录一次接线 src/logger.ts (log.setServer() 包裹 server.tool), 因此,它统一涵盖了所有26个工具,包括那些处理程序从未记录的工具 他们自己。它是 默认情况下关闭:它很吵,结果可以携带页面数据 (令牌、存储状态、响应体)。放大 TOOL_RESULT_PREVIEW 在 src/limits.ts 以记录更多的每个结果。

你甚至可能不需要这个文件。 每条记录线都是 也 发送到已连接的 MCP客户端作为 notifications/message,与 data 作为结构化对象。所以与 LOG_TOOL_RESULTS=1,代理自己的客户端(例如OpenCode)已经收到 在MCP日志面板中跟踪并将其呈现为可扩展的JSON——之前请检查 伸手去够 tail(这些是协议级通知;它们不会进入 模型的上下文。)

为什么是日志,而不是检查员? 因为检查员 不能 看a 代理的会话——MCP没有观察者角色,HTTP也不会改变这一点。看 MCP检查员的警告。服务器自己的日志是 只有代理的呼叫可见的地方,这就是观察存在于这里的原因。

环境变量

变量 默认值 描述
CDP_HOST 127.0.0.1 Chrome主机(见下面的IPv4注释)
CDP_PORT 9222 Chrome远程调试端口
CDP_TARGET_URL (无) 匹配特定选项卡的URL子字符串
LOG_LEVEL info debug / info / warn / error --控制stderr输出
LOG_FILE (无) 写入日志的路径
LOG_TOOL_RESULTS false 1 要记录每个工具调用的结果,请参见 观看代理会话

所有这些都会被解析一次 src/env.ts 并暴露为打字 ENV 对象。

使用 127.0.0.1,不 localhost. Chrome浏览器 --remote-debugging-port 绑定到 IPv4环回(127.0.0.1)只有。在许多系统上,尤其是Windows 节点18+-- localhost 解析为IPv6环回(::1) 第一,因此连接 到 localhost 失败与 ECONNREFUSED 尽管Chrome正在运行。默认值 是 127.0.0.1 出于这个原因;仅当Chrome在另一台主机上时才更改它。

.env 文件(推荐)。 复制 .env.example 到 .env 并编辑它——四个 start* 脚本通过Node的内置程序加载它 node --env-file-if-exists=.env,所以一个 编辑适用于每种运行模式。 .env 被忽视; .env.example 是否已承诺 模板。已在shell/CI中设置变量 胜利 结束 .env (节点不 覆盖现有环境),如果 .env 如果不存在,脚本仍将使用默认值运行 上面。

cp .env.example .env   # then edit values
yarn start             # picks up .env automatically

在启动时热切地连接到Chrome。如果Chrome尚未就绪,请在第一次工具调用时重试。要切换选项卡,请更改 CDP_TARGET_URL 在 .env (或环境)并重新启动服务器。

集中限制(src/limits.ts)

所有可调上限、超时、截断阈值和默认值都在一个文件中定义-- src/limits.ts这使得在不遍历代码库的情况下查看和调整运行时行为变得容易。

文件分为两部分:

浏览器端常量 被退回 browserLimits() 功能。因为代码片段代码通过CDP在浏览器页面内运行 Runtime.evaluate(),这些值必须存在于bundler可以字符串化的自包含函数中。(TypeScript类型的注释在transpile时会被擦除,因此字符串化的输出仍然是干净的浏览器JS——请参阅Project Structure下的注释。)每个使用有界序列化、钩子提取、prop walking或DOM爬升的代码段都会从以下位置读取其限制 browserLimits().

常量 默认值 它控制什么
SERIALIZE_MAX_DEPTH 6 safeSerialize 最大对象嵌套深度
SERIALIZE_MAX_CHARS 2000 safeSerialize 近似焦炭预算
SERIALIZE_STRING_TRUNCATE 300 safeSerialize 单个字符串值上限
SERIALIZE_MAX_ARRAY_ITEMS 50 safeSerialize 最大数组元素数
SERIALIZE_MAX_OBJECT_KEYS 30 safeSerialize 每个对象的最大键数
PROPS_SERIALIZE_DEPTH 4 safeProps 每值深度限制
PROPS_SERIALIZE_CHARS 500 safeProps 每值char预算
PROPS_MAX_KEYS 30 safeProps 最大道具键
FUNCTION_BODY_MAX_LENGTH 1000 describeFn 函数体截断
MAX_HOOKS_PER_FIBER 20 extractHooks 每个组件的最大钩数
SELECTOR_MAX_DOM_STEPS 200 fiberToSelector DOM父母人数最多
ERROR_BOUNDARY_MAX_DEPTH 50 误差边界行走最大深度
FIND_COMPONENTS_DEFAULT_MAX 20 findComponents 默认结果上限

服务器端常量 被退回 serverLimits() 功能。不像 browserLimits() --哪个 必须 是一个函数,因此可以将其字符串化为浏览器代码段-- serverLimits() 是一个纯粹用于对称和分组的函数。消费者在模块加载时破坏他们需要的东西,例如。 const { MAX_NETWORK_BUFFER } = serverLimits();:

常量 默认值 它控制什么
MAX_NETWORK_BUFFER 500 内存环缓冲区中的网络收集器大小
NETWORK_BODY_TRUNCATE 5000 非JSON响应体截断
BREAKPOINT_SCOPE_MAX_PROPS 30 断点命中时自动获取作用域属性
STEP_TIMEOUT_MS 10000 跨步/进入/退出重新暂停超时
SOURCE_DEFAULT_LINE_WINDOW 200 返回的默认行数 read_source
SOURCE_SEARCH_MAX_SCRIPTS 100 每个用户搜索的最大脚本数 search_source 呼叫
SOURCE_SEARCH_MAX_RESULTS 100 来自的最大总匹配数 search_source
LOG_EXPRESSION_TRUNCATE 200 日志输出中的表达式字符串截断
TOOL_RESULT_PREVIEW 800 完成时记录每个工具结果的字符数
TREE_DEFAULT_MAX_DEPTH 4 get_component_tree 默认最大深度
FIND_DEFAULT_MAX_RESULTS 20 find_react_component 默认最大结果
NETWORK_DEFAULT_LIMIT 20 get_network_responses 默认限额
BREAKPOINT_DEFAULT_TIMEOUT_S 60 wait_for_breakpoint 默认超时(秒)

项目结构

bin/
└── cli.mjs                    # `react-debug-mcp` executable: registers tsx, imports src/index.ts

src/
├── index.ts                   # Entry point
├── env.ts                     # Parses all env vars once → typed ENV object
├── limits.ts                  # Centralized limits & tunables (single source of truth)
├── cdp-client.ts              # Lazy CDP connection (memoized) + evaluate()
├── logger.ts                  # Dual-mode logging (MCP notifications + stderr)
├── url-filters.ts             # Shared browser-internal URL-prefix filters
├── types/
│   ├── chrome-remote-interface.d.ts # Type shim (package ships no types)
│   └── globals.d.ts           # Window augmentations for browser-side snippet globals
├── collectors/
│   ├── network.ts             # Captures API requests/responses (live + historical backfill)
│   └── debugger/              # CDP Debugger domain (split into sub-modules)
│       ├── index.ts           # Barrel: attach() + re-exports
│       ├── state.ts           # Shared mutable state (CDP client, script map, pause state)
│       ├── scripts.ts         # Script tracking, source map resolution, URL helpers
│       ├── breakpoints.ts     # Set/remove breakpoints and logpoints (source map fallback)
│       ├── pause.ts           # Wait, step, resume, scope inspection
│       └── source-reading.ts  # Read and search bundled script source (parallel search)
├── tools/
│   ├── react.ts               # React component inspection (8 tools)
│   ├── store.ts               # State store reading (1 tool)
│   ├── network.ts             # Network response querying (1 tool)
│   ├── debugger.ts            # Breakpoints, source reading + logpoints (15 tools)
│   └── general.ts             # evaluate_in_page (1 tool)
└── snippets/                  # Browser-side code that runs inside the page
    ├── index.ts               # Barrel: bundles functions → CDP-ready strings
    ├── bundle.ts              # fn.toString() bundler utility
    ├── helpers.ts             # Shared helpers (fiber lookup, display name, hooks, …)
    ├── page-info.ts           # get_page_info snippet
    ├── component-tree.ts      # get_component_tree snippet
    ├── component-path.ts      # get_react_component_path snippet
    ├── find-components.ts     # find_react_component snippet
    ├── inspect-by-name.ts     # inspect_react_component_by_name snippet
    ├── inspect-by-selector.ts # inspect_react_component snippet
    ├── inspect-context.ts     # inspect_react_context snippet
    ├── error-boundaries.ts    # get_react_error_boundaries snippet
    └── store-reader.ts        # get_store_state snippet

TypeScript和代码段打包器

这个 src/snippets/ 功能(和 browserLimits() 在 src/limits.ts)都是 序列化为 fn.toString() 并通过CDP注入页面。定向 ES2022 意味着没有降级,因此类型注释被剥离而没有 __awaiter-风格助手被注入到功能体中。

但“没有注射助手”并不完全正确,例外情况也很严重。 多伦多证券交易所 硬代码esbuild keepNames: true,保存 fn.name 通过重写 每 命名嵌套函数 进入 __name(fn, "fn") 调用并发出 __name 助手at 模块范围。模块作用域不存在 fn.toString(), 因此,幸存的呼叫到达未定义的页面:

Uncaught ReferenceError: __name is not defined
    at componentTree (<anonymous>:16:838)

bundle() 因此,船舶是自己建造的 __name 每个包内的定义, 和 在导入时抛出 如果esbuild注入了它不提供的帮助程序-- 转向未来的浏览器端 ReferenceError 进入拒绝启动的服务器。 如果您看到该错误,请将缺少的定义添加到 ESBUILD_HELPERS 在 src/snippets/bundle.ts.

编辑代码段代码时:不要使用TS枚举,只引用助手 在每个片段中列出 deps 数组(缺失的dep解析良好,只会失败 ReferenceError 当代码段实际在页面中运行时)。

日志点输出格式

日志点使用结构化前缀写入控制台,以便代理可以找到它们:

⚡RDM|<label>|<timestamp>|<JSON data>

代理人搜索 ⚡RDM 在chromedevtools mcp的控制台输出中。当多个日志点处于活动状态时,标签有助于关联哪个日志点产生了哪些输出。

许可证

麻省理工学院