做前端调试这么多年,遇到最烦的场景就是 H5 页面在真机上出问题,手机连不上电脑,日志看不到,接口状态全靠猜。vConsole 大家都熟,一个悬浮按钮把 console、network、storage 全摊在页面上,但问题来了——它只输出给人看,AI 看不见。最近我给 vConsole 加了个 MCP 能力,让 AI agent 能直接读到 H5 的日志和请求,然后再帮我把问题定位出来。这套组合拳打下来,调试效率是真的上了一个台阶。
下面我会把这个方案的完整思路、核心代码、踩坑记录全部拆开讲,适合在做 AI 编程工具、H5 调试工具,或者自己搭 agent 的开发者参考。不保证代码零改动就能跑,但整个链路和关键点的解法,是可以直接复用的。
1. 这个项目到底在解决什么问题
1.1 传统 H5 调试的痛点
H5 调试最常见的方式是连上 Chrome DevTools 的远程调试,或者用 vConsole 在页面上看实时日志。但这两条路都有明显的坑:
- 远程调试需要数据线、USB 授权、Chrome 版本匹配,一套流程下来少说五分钟,现场给客户演示的时候根本等不起。
- vConsole 只能看,不能“想”。日志从你眼前滚过去,你得自己从一堆警告里挑出哪条是关键错误,再根据 network 面板的请求参数去猜后端是不是返回了异常。
- 更尴尬的是,很多 H5 跑在小程序 web-view、钉钉内置浏览器或者第三方 App 的 WebView 里,这些环境根本没有 DevTools 可用,vConsole 基本成了唯一选择。
1.2 为什么需要把 vConsole 接入 MCP
MCP(Model Context Protocol)是这两年 AI 工具链里火热的标准协议,简单说就是给 AI 模型开了一扇“访问外部工具和数据”的窗。比如让 AI 调用一个函数去读文件、查数据库、执行命令,现在通过 MCP server 就能标准化完成。
vConsole 恰好是一个数据富矿——它采集了页面的 console 日志、网络请求、cookie、localStorage,甚至还有性能数据。如果把这些数据通过 MCP 暴露给 AI,AI 就能直接“看到”页面实际发生了什么,而不是靠你粘贴一段报错文本再发挥想象力。
所以这个项目的本质就是:在 vConsole 和 AI agent 之间架一座桥,让 AI 拿到 H5 运行时的第一手现场数据,再基于这些数据做错误定位、原因分析和修复建议。
1.3 适用场景和受众
这套方案我实测下来最爽的使用场景有三个:
- 远程协助调试:同事或客户那边的 H5 出了诡异问题,你不用远程桌面,只要对方把 MCP 服务跑起来,你就能让 AI 直接看现场日志。
- 自动化质量分析:把项目接入 CI 流程,每次构建后自动跑一轮 H5,让 AI 根据 vConsole 采集到的请求失败率、JS 异常给出质量报告。
- AI 编程助手增强:在 Cursor、Codex 或自建 agent 里挂上这个 MCP server,AI 改完代码后能立即查看 H5 运行结果,形成“改码-运行-看日志-再改码”的闭环。
适合的读者是:玩过 MCP 但觉得例子太玩具、想找个真实场景练手的;被 H5 真机调试折磨过的前端;以及在做 AI agent 但苦于无法获取运行时数据的朋友。
2. 方案选型与整体架构
2.1 为什么选 vConsole 而不是 Puppeteer 或 WebDriver
乍一看,让 AI 看到 H5 日志,用 Puppeteer 控制浏览器然后灌给 AI 不是更简单?我也这么想过,但实际跑了几个方案,发现 vConsole 有它不可替代的优势:
| 维度 | vConsole + MCP | Puppeteer / WebDriver |
|---|---|---|
| 运行环境 | 任意 WebView、移动端、浏览器 | 仅限桌面端可控制浏览器 |
| 侵入性 | 只需引入一个 JS | 需要完整的自动化框架 |
| 实时性 | 页面内直接采集,无延迟 | 需要轮询或会话管理 |
| 远程场景 | 客户手机也能用 | 基本只适合本机 |
| 部署成本 | 前端三分钟搞定 | 要装浏览器、驱动、服务端 |
vConsole 本身就是给移动端调试设计的,你在微信里、钉钉里、App 内嵌 WebView 里都能用它。MCP 服务端只需要在能访问到采集数据的机器上跑,本地跑一个轻量 Node 服务就够,完全不需要动页面逻辑。
2.2 MCP 协议的三个核心概念
做 MCP server 前,得先理解协议里最关键的三个词:Tools(工具)、Resources(资源)、Prompts(提示模板)。
- Tools 是让 AI 主动调用的函数,比如我的 server 里定义了
get_logs、get_network_requests、clear_logs这几个工具。AI 根据用户问题决定调用哪个,参数由 AI 自己填。 - Resources 是 AI 可以读取的上下文数据,比如注册一个
vconsole://logs的资源 URI,AI 在分析时会把里面的文本当作知识读进来。 - Prompts 是预设的指令模板,比如定义一个“分析异常日志”的模板,里面写清楚让 AI 关注哪些错误类型。
我们的目标是让 AI主动去拉取数据,所以核心要写的是 Tools。但 Resources 也建议注册一份,因为有些 AI 客户端会对 resources 做自动加载,这样 AI 开聊之前就已经知道了页面基础情况。
2.3 整体架构:vConsole 采集 + WebSocket 转发 + MCP Server
我的最终选型是:
- 页面端:vConsole 开启后,通过它暴露的 plugin 事件钩子拿到日志和网络数据,然后把这些数据封装成固定格式,通过 WebSocket 发送到本地 MCP Server。
- 本地端:一个用 Node.js 写的 MCP Server,监听 WebSocket 端口接收数据,缓存到内存队列,同时暴露 MCP 工具给 AI 调用。
- AI 客户端:任何支持 MCP 的 agent,比如 Claude Desktop、Cursor、Cherry Studio,配置上这个 server 的地址就能开始对话。
选 WebSocket 而不是 HTTP 轮询,是因为日志是持续产生的,WebSocket 一条长连接就能实时推送,避免掉 HTTP 频繁握手。而且 H5 页面刷新后 WS 会自动重连,数据链路不会断。
3. 核心细节解析:MCP Server 与 vConsole 数据格式
3.1 vConsole 数据采集的关键代码
vConsole 提供了VConsole.pluginAPI,你可以写一个插件来监听 console 和 network 事件。核心思路是把 vConsole 内部的事件源给接管过来。
// h5-init.js import VConsole from 'vconsole'; import { WS_SENDER } from './ws-sender.js'; const vConsole = new VConsole({ useDefaultPlugins: true }); // 监听 console 日志 vConsole.on('consoleLog', (logData) => { WS_SENDER.send({ type: 'console', level: logData.logType, // log / info / warn / error content: logData.logs.map(String), timestamp: Date.now() }); }); // 监听网络请求 vConsole.on('networkRequest', (req) => { WS_SENDER.send({ type: 'network', method: req.method, url: req.url, status: req.status, request: req.requestText, response: req.responseText, duration: req.time, timestamp: Date.now() }); });这里有个容易踩的坑:vConsole 的networkRequest事件在网络请求完成时才会触发,所以如果你要看到“慢了 3 秒才失败的请求”,得等它超时结束。对调试来说这不算大问题,但要注意 MCP server 端的缓存策略,别把超时请求的迟到数据丢了。
3.2 WebSocket 转发模块
页面端负责采集,本地端负责接收,中间用一条 WS 通道连接。这里我把发送端的逻辑封装成一个独立模块:
// ws-sender.js let ws = null; const queue = []; const MAX_QUEUE = 100; export const WS_SENDER = { init(url) { ws = new WebSocket(url); ws.onopen = () => { // 连接成功后,把积压的数据一口气发过去 while (queue.length) ws.send(queue.shift()); }; ws.onclose = () => { // 断线自动重连 setTimeout(() => this.init(url), 1000); }; }, send(data) { const payload = JSON.stringify(data); if (ws && ws.readyState === WebSocket.OPEN) { ws.send(payload); } else { queue.push(payload); if (queue.length > MAX_QUEUE) queue.shift(); // 防止内存爆掉 } } };为什么设置 MAX_QUEUE 上限?因为 H5 页面如果处于离线状态或者本地服务没启动,日志会一直堆积。内存被打爆只是时间问题。我实测过一个长期挂着的页面,峰值每秒能产生 200 条以上日志,不加限制的话十几秒就能吃掉上百 MB 内存。
3.3 MCP Server 的完整实现
下面这段是基于官方 TypeScript SDK 写的核心 server,断点续传和工具定义都在这:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { WebSocketServer } from "ws"; import { z } from "zod"; // 内存缓存 const logStore: any[] = []; const networkStore: any[] = []; // 创建 MCP Server const server = new McpServer({ name: "vconsole-mcp", version: "1.0.0", }); // 工具 1:获取 console 日志 server.tool( "get_logs", { since: z.number().optional() }, async ({ since = 0 }) => { const filtered = logStore.filter((l) => l.timestamp >= since); return { content: [ { type: "text", text: JSON.stringify(filtered.slice(-50), null, 2), }, ], }; } ); // 工具 2:获取网络请求 server.tool( "get_network_requests", { urlMatch: z.string().optional() }, async ({ urlMatch }) => { const filtered = networkStore .filter((r) => (urlMatch ? r.url.includes(urlMatch) : true)) .slice(-50); return { content: [ { type: "text", text: JSON.stringify(filtered, null, 2), }, ], }; } ); // 工具 3:清空缓存 server.tool("clear_cache", async () => { logStore.length = 0; networkStore.length = 0; return { content: [ { type: "text", text: "cleared" } ], }; }); // WebSocket 接收 H5 日志 const wss = new WebSocketServer({ port: 9888 }); wss.on("connection", (socket) => { socket.on("message", (data) => { const msg = JSON.parse(data.toString()); if (msg.type === "console") logStore.push(msg); if (msg.type === "network") networkStore.push(msg); }); }); // 启动 MCP over stdio const transport = new StdioServerTransport(); await server.connect(transport);这段代码解决了几个关键问题:
- AI 拿到的数据量是有限的:每次最多返回 50 条记录,进制 50 条幂等。为什么是 50?因为大模型的上下文窗口有限,一次性塞几百条日志进去,AI 只会被噪音淹没。真实调试时,你更关心的通常是最近的失败请求和最新的报错。
- 时间戳过滤:
since参数让 AI 能精确提取“某个时间点之后”的日志。比如 AI 问“刚才页面加载时有没有报错”,它就会带着当前时间戳去调get_logs。 - 缓存清理:调试完一个页面要切换场景,直接
clear_cache,比重启服务方便得多。
4. 实操过程:把整套链路跑起来
4.1 环境准备
你需要准备的环境非常轻量:
- Node.js 18 以上,因为用到了
WebSocketServer和 top-level await。 - 一个支持 MCP 客户端的软件,比如 Claude Desktop、Cherry Studio 或者 Cursor。
- 一个 H5 页面,随便拿个本地开发服务器跑起来就行。
安装依赖只需要两条命令:
npm install @modelcontextprotocol/sdk ws zod4.2 启动 MCP Server 并验证 WebSocket 接入
先启动 server:
ts-node mcp-server.ts这条命令会在 stdio 通道上启动 MCP 服务,同时开启 9888 端口的 WebSocket 监听。怎么验证 WS 通不通?在浏览器控制台里手动建一条连接试试:
const ws = new WebSocket('ws://localhost:9888'); ws.onopen = () => ws.send(JSON.stringify({ type: 'console', level: 'error', content: ['手动测试'], timestamp: Date.now() }));如果你在 server 端打了个日志断点,能看到这条消息进来,说明页面到服务端的链路已经打通。
4.3 在 AI 客户端里配置 MCP Server
以 Cherry Studio 为例,它现在支持直接注册本地 MCP:
- 进入“设置 - MCP 服务器”
- 添加新服务器,传输方式选择 “Stdio”
- 命令填
npx ts-node /path/to/mcp-server.ts,也可以用node mcp-server.js,前提是编译成 JS。
配置好之后刷新会话,AI 客户端会自动探测到 server 上声明的三个工具。你试着问一句:“帮我看一下当前页面的最新报错”,AI 就会去调用get_logs工具,然后基于返回内容做分析。
4.4 一个完整的调试实例
我拿一个真实业务场景演示。页面在提交订单时报 “500”,但手机上看不到具体错误对象结构。我把 vConsole 接上 MCP 后,AI 的调用链是这样的:
- 用户提问:“为什么订单提交失败?”
- AI 调用
get_network_requests,参数urlMatch: "order"。 - 工具返回了最近 50 条请求,其中一条是
POST /api/order,状态码 500,响应体是{"code":-1,"msg":"request failed with status code 500"}。 - AI 又从
get_logs里读取报错上下文,发现有一条Uncaught TypeError: Cannot read properties of undefined (reading 'data'),位置在submitOrder.js:42。 - AI 得出结论:接口返回的结构里没有
data字段,前端直接读response.data导致抛错,然后自动抛出修复建议——加一个空值判断。
整个过程没有打开一次 DevTools,没有截图,没有复制粘贴。AI 看到了现场日志、请求参数、响应体,给出的定位比多数初级开发看得还准。
4.5 在小程序 WebView 和 App 内嵌页面的适配
如果你要调试的场景是小程序里嵌套的 H5,或者某个 App 的 WebView,需要额外注意两件事:
- WebView 的 origin 可能不是你本地服务的地址,所以 WebSocket 连接不能走域名限制。我这里默认允许所有来源连接,通过配置项
wss.verifyClient关闭验证,只在内网调试时用。 - WebView 的 JS 可能被缓存,你改动 vConsole 采集代码后要强制刷新。最简单的办法是在 URL 后面加个随机参数,比如
?_v=1.0.0,或者直接用 location.reload 配合服务端 no-cache 头。
5. 常见问题与排查技巧实录
5.1 MCP 工具能发现但调用超时
工具列表能被 AI 发现,说明拓扑连接是通的。调用超时大概率是 server 端卡在了 WebSocket 的某个操作上。曾在服务端开了同步文件写入,导致日志缓存的大量写盘操作阻塞了 MCP 工具响应。最后把磁盘写入改成异步批处理,超时就解决了。
5.2 vConsole 数据传不上来
先检查页面有没有报跨域错。本地调试场景,MCP 服务监听的是localhost:9888,如果页面从局域网 IP 访问,要确保服务端绑定0.0.0.0。另外,H5 页面如果是 HTTPS 环境,WebSocket 也必须用 WSS,否则浏览器直接拦截混合内容。
5.3 数据量太大,AI 分析不过来
我踩过一次坑,某个业务页面每分钟产生几百条日志,AI 每次都要从上万条历史记录里翻,既费 token 又容易给出错误结论。后来在工具定义里加了一个强约束:默认只返回最近 N 条,并把时间范围的提示词写清楚,让 AI 必须带since参数来限定查询窗口。
5.4 H5 页面刷新后日志丢失
WebSocket 连接虽然重连了,但内存缓存是纯前端变量,刷新后所有历史都没了。这个问题的解法分两种:
- 调试场景够用就行,通常不需要保留刷新前的日志。
- 如果要排查页面刷新后立即崩溃的问题,那得改采集策略——页面启动时先加载一段独立脚本,提前建立 WS 连接并缓存到 sessionStorage,遇到报错先把日志写进 sessionStorage,刷新后再补发。
我实际用方案二做过一次崩溃排查,效果显著。当时页面白屏,console 连输出都没有,就是因为刷新太快 WS 还没连上。提前缓存的思路救了这个场景。
5.5 安全边界一定要划清楚
MCP server 一旦跑起来,意味着 AI 拥有读取你本地调试数据的权限。有三条经验分享:
- 不要在生产环境开启这个服务,尽量只在开发、测试模式下挂载。
- WebSocket 建议只绑定本机回环地址,内网使用时通过 SSH 隧道转发,避免暴露到公网。
- 工具设计上要只读。我的 server 刻意不做“发请求”和“改代码”的工具,只保留日志读取的能力,防止 AI 在调试时误操作。
6. 经验总结与后续扩展
6.1 这个方案的真正价值在哪
用 vConsole MCP 最大的感受是:把调试从“人看日志”变成了“AI 读现场”。过去需要人眼在满屏日志里找规律,而现在 AI 可以直接拿数据和代码上下文做交叉分析,甚至能并行处理多个页面的日志,这在人工协作下几乎不可能。
坑也给你避了几个:
- WebSocket 缓冲队列一定要设上限,否则运行半个小时后内存就爆。
- 日志返回量要克制,宁可让 AI 多问几次,也不要一次性塞给它 500 条。
- 时间戳是精髓,所有工具都要支持按时间过滤,这是提高分析准确率的钥匙。
6.2 顺着这个思路还能玩出什么花
我在版本迭代里准备加三个新工具:
- 一个是
get_page_performance,把 vConsole 的 Performance 数据暴露出来,AI 能直接分析首屏耗时、LCP 等指标。 - 一个是
get_storage,读取 localStorage 和 sessionStorage,排查登录态和缓存问题。 - 还有一个是
diff_logs,把两次调试会话的日志做差集,定位“为什么这次不行但上次行”。
这套东西配合支持 MCP 的 agent,比如 Codex 或者自建 agent,你甚至可以做到让 AI 自己复现问题、看日志、改代码、再跑一轮验证,基本上就是往 AI 驱动的前端质量闭环又近了一步。
如果你也在做 H5 调试相关的事,强烈建议把这条链路跑一遍,不一定非要照抄,只要理解“vConsole 采集 + WebSocket 转发 + MCP 工具化”这个模式,以后任何运行时数据都可以这么喂给 AI。调试这行饭,真是越来越靠工具了。