
1. 移动端调试的痛点与破局思路做过 H5 的同学都懂那种感觉本地跑得好好的页面一上真机就各种玄学问题。按钮点不动、接口偶尔 500、白屏闪一下又好了最要命的是——你根本看不到控制台。手机连电脑、开远程调试、装驱动、配端口一套流程走下来问题可能已经复现不出来了。更别提有些场景压根连不上调试器比如嵌在 App WebView 里的页面、第三方渠道的 H5、用户手机上的偶发 bug。传统方案无非这么几种一是用alert大法把变量一个个弹出来看效率低到令人发指二是引入 vConsole 这类移动端调试面板在页面右下角挂一个悬浮按钮点开就能看日志、网络请求、DOM 结构。vConsole 确实好用我自己在项目里也用了好几年但它有个天然局限——你得用眼睛去看。日志多了要翻请求多了要筛而且它只存在于那台设备上你没法把信息同步到别的地方做进一步分析。这两年 AI 辅助编程越来越普及我就在想一个问题能不能让 AI 直接“看见” H5 页面里发生了什么不是我把日志复制粘贴给它而是它自己就能读取 vConsole 里的日志和网络请求然后帮我分析问题出在哪。这个想法听起来有点科幻但 MCPModel Context Protocol的出现让这件事变得可行。MCP 本质上是一套让 AI 模型和外部工具、数据源对话的协议你可以把它理解成 AI 的“USB 接口”——只要设备支持这个接口AI 就能直接调用它。所以这个项目的核心思路就很清晰了把 vConsole 采集到的日志和网络请求通过 MCP 协议暴露出去让 AI 能够实时读取并参与 debug。这样一来你不需要把日志复制来复制去AI 直接就能看到页面在真机上到底发生了什么。对于经常和 H5 打交道的前端、测试、甚至产品同学来说这套东西能省下大量沟通和排查成本。接下来我会从整体设计、核心实现、实操步骤、踩坑经验几个维度把整个方案拆开讲清楚。2. 整体架构设计与技术选型考量2.1 为什么是 vConsole MCP 这个组合先说 vConsole。它是一个轻量级的移动端调试面板核心能力是劫持console对象、拦截XMLHttpRequest和fetch、监听window.onerror然后把收集到的信息渲染成一个可交互的面板。它的优势在于侵入性小、接入简单一行代码就能挂上去而且对性能影响可控。市面上类似的还有 Eruda功能更全但体积也更大。我选 vConsole 主要是因为它足够轻而且 API 设计比较清晰方便我做二次开发。再说 MCP。MCP 是 Anthropic 推出的开放协议目的是让 AI 模型能够安全、标准化地访问外部工具和数据。它的架构是典型的客户端-服务端模式MCP Server 负责暴露资源Resources和工具ToolsMCP Client比如 Claude Desktop、Cursor 等负责调用。对于我们的场景来说vConsole 采集的数据就是“资源”AI 通过 MCP 协议来读取这些资源就能实现“看见日志”的效果。那为什么不用 WebSocket 直接推给 AI 呢因为 AI 模型本身并不具备主动连接 WebSocket 的能力它需要一个中间层来桥接。MCP 就是这个中间层它把“读取日志”这个动作标准化成了 AI 可以理解的接口。当然底层的数据传输我确实用了 WebSocket因为 H5 页面和本地服务之间需要实时通信WebSocket 的全双工特性正好合适。2.2 数据流转的完整链路整个系统的数据流是这样的H5 页面加载 vConsole 的定制版本这个版本在原有功能基础上增加了一个 WebSocket 客户端。当页面产生日志或网络请求时vConsole 照常收集同时通过 WebSocket 把数据推送到本地运行的 MCP Server。MCP Server 收到数据后一方面缓存起来另一方面通过 MCP 协议暴露给 AI 客户端。AI 客户端在需要的时候调用 MCP 工具就能拿到最新的日志和请求列表。这里有个关键设计点数据是推还是拉。我选择的是推拉结合——WebSocket 负责实时推送保证数据不丢MCP 工具负责按需拉取保证 AI 拿到的是它真正需要的部分。如果只推不拉AI 会被大量无关日志淹没如果只拉不推又可能错过瞬时错误。推拉结合的好处是MCP Server 可以维护一个环形缓冲区只保留最近 N 条记录AI 查询时按时间戳或关键字过滤既高效又不会丢关键信息。另一个设计点是多页面支持。实际项目中经常同时开好几个 H5 页面每个页面都有自己的 vConsole 实例。我在 WebSocket 连接建立时会给每个页面分配一个唯一的clientIdMCP Server 按clientId分组管理数据。AI 查询时可以指定clientId也可以查询所有页面的汇总信息。这个设计在调试多页面应用时特别有用比如你可以在一个页面操作然后在另一个页面观察接口返回。2.3 安全边界与本地化部署安全方面我做了几层考虑。首先MCP Server 只监听本地回环地址不对外网开放避免日志数据泄露。其次WebSocket 连接需要携带一个简单的 token这个 token 在服务启动时生成页面接入时需要配置。虽然不是什么强安全机制但能挡住大部分误连和扫描。最后所有数据只存在内存里不落盘服务重启即清空避免敏感信息残留。本地化部署还有个好处是延迟低。WebSocket 走本地回环日志从页面到 MCP Server 基本是毫秒级AI 查询时几乎感觉不到延迟。我实测下来从页面console.log到 AI 能读到整个过程在 50ms 以内对于 debug 场景完全够用。3. 核心细节解析与实操要点3.1 vConsole 定制版的改造要点原版 vConsole 是不带 WebSocket 推送能力的所以第一步是改造它。我的做法是继承 vConsole 的VConsole类重写它的console插件和network插件在原有逻辑之后追加推送逻辑。具体来说console插件在printLog方法里除了渲染到面板还会调用ws.send()把日志对象序列化后发出去。network插件则在onResponse回调里推送请求详情。这里有个细节要注意序列化时要处理循环引用。日志里经常会有 DOM 对象、Window 对象直接JSON.stringify会报错。我的做法是写一个safeStringify函数遇到循环引用就替换成[Circular]遇到函数就替换成[Function]遇到 DOM 节点就取tagName和id。这样既能保留关键信息又不会因为序列化失败丢日志。另一个细节是日志分级。vConsole 本身支持log、info、warn、error等级别我在推送时也保留了这个字段。MCP Server 收到后按级别分类存储AI 查询时可以只查error级别的日志快速定位问题。实测下来这个过滤功能在日志量大的时候特别有用能把排查范围从几百条缩小到几条。3.2 MCP Server 的资源与工具设计MCP Server 这边我定义了两个核心资源logs和requests。logs资源返回所有日志的列表支持按clientId、level、keyword过滤requests资源返回所有网络请求支持按clientId、status、url过滤。资源的设计遵循 MCP 规范用 URI 模板来暴露比如vconsole://logs/{clientId}就能拿到指定页面的日志。工具方面我定义了三个get_logs、get_requests、clear_data。get_logs接受clientId、level、keyword、limit四个参数返回过滤后的日志数组。get_requests类似多了status参数用来筛选 HTTP 状态码。clear_data用来清空指定页面的缓存方便开始新一轮调试。工具的参数设计尽量简单因为 AI 调用时不会做太复杂的推理参数越直观越好。这里有个经验工具描述要写清楚。MCP 协议里每个工具都有description字段AI 会根据这个描述来决定什么时候调用。我一开始写得太简略AI 经常不知道该用哪个工具。后来我把每个参数的用途、返回值的格式、典型使用场景都写进去AI 的调用准确率明显提升。比如get_logs的描述里我写了“当用户询问页面报错、日志输出、console 信息时使用此工具”AI 就能在合适的时机自动调用。3.3 WebSocket 通信的稳定性保障WebSocket 连接是整套系统的命脉一旦断了日志就推不过来。所以我做了几层保障。第一层是心跳机制客户端每 30 秒发一次 ping服务端回 pong如果连续两次没收到 pong就认为连接已断触发重连。第二层是自动重连客户端检测到onclose事件后延迟 1 秒重连重连成功后把断线期间的日志补推上去。第三层是消息队列如果 WebSocket 暂时不可用日志先存到本地队列等连接恢复后再批量发送。心跳间隔的选择也有讲究。太短了浪费资源太长了检测不及时。我试过 10 秒、30 秒、60 秒最后定在 30 秒。因为 H5 页面在后台时浏览器可能会节流定时器30 秒是个比较平衡的值既不会太频繁又能在页面回到前台时快速恢复。另外重连时的补推逻辑要注意去重我给每条日志加了自增 ID服务端收到后按 ID 去重避免重复记录。还有一个坑是页面刷新时的连接清理。H5 页面刷新后旧的 WebSocket 连接会断开但服务端可能还没感知到。我的做法是在beforeunload事件里主动发送一个close消息服务端收到后立即清理对应的clientId数据。如果不做这一步服务端会残留很多僵尸连接时间长了内存会涨。4. 完整实操流程与关键环节实现4.1 环境准备与依赖安装先列一下需要的东西。Node.js 版本建议 18 以上因为 MCP SDK 用了一些较新的 API。包管理用 npm 或 pnpm 都行我习惯用 pnpm速度快一些。核心依赖有三个modelcontextprotocol/sdk用来实现 MCP Serverws用来做 WebSocket 服务vconsole作为基础库。另外还需要一个 MCP 客户端来测试我用的是 Claude Desktop你也可以用 Cursor 或其他支持 MCP 的工具。安装命令很简单pnpm init pnpm add modelcontextprotocol/sdk ws vconsole pnpm add -D typescript types/ws types/nodeTypeScript 配置里记得把target设为ES2022module设为NodeNext这样能直接用顶层的await。tsconfig.json的关键配置如下{ compilerOptions: { target: ES2022, module: NodeNext, moduleResolution: NodeNext, outDir: ./dist, strict: true, esModuleInterop: true } }4.2 MCP Server 的核心代码实现先写 WebSocket 服务部分。创建一个WebSocketServer实例监听 8765 端口。每个连接进来时从 URL 参数里取clientId然后把这个连接存到一个 Map 里。收到消息时解析 JSON根据type字段分发到不同的处理函数。import { WebSocketServer, WebSocket } from ws; const clients new Mapstring, WebSocket(); const logBuffer new Mapstring, any[](); const requestBuffer new Mapstring, any[](); const wss new WebSocketServer({ port: 8765 }); wss.on(connection, (ws, req) { const url new URL(req.url!, http://${req.headers.host}); const clientId url.searchParams.get(clientId) || default; clients.set(clientId, ws); if (!logBuffer.has(clientId)) logBuffer.set(clientId, []); if (!requestBuffer.has(clientId)) requestBuffer.set(clientId, []); ws.on(message, (data) { const msg JSON.parse(data.toString()); if (msg.type log) { const buffer logBuffer.get(clientId)!; buffer.push(msg.payload); if (buffer.length 1000) buffer.shift(); } else if (msg.type request) { const buffer requestBuffer.get(clientId)!; buffer.push(msg.payload); if (buffer.length 500) buffer.shift(); } }); ws.on(close, () { clients.delete(clientId); }); });这段代码的关键点是环形缓冲区。日志最多存 1000 条请求最多存 500 条超出就丢掉最旧的。这样既能保证内存可控又不会因为日志太多导致查询变慢。实际调试时1000 条日志足够覆盖大部分场景如果不够可以调大但要注意内存占用。接下来是 MCP Server 部分。用modelcontextprotocol/sdk创建 Server 实例注册资源和工具。资源用server.resource()注册工具用server.tool()注册。每个工具的回调函数里从缓冲区读取数据按参数过滤后返回。import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; const server new Server( { name: vconsole-mcp, version: 1.0.0 }, { capabilities: { resources: {}, tools: {} } } ); server.tool( get_logs, 获取指定页面的控制台日志。当用户询问页面报错、日志输出、console 信息时使用此工具。, { clientId: { type: string, description: 页面标识不传则返回所有页面 }, level: { type: string, description: 日志级别log/info/warn/error }, keyword: { type: string, description: 关键字过滤 }, limit: { type: number, description: 返回条数默认 50 } }, async ({ clientId, level, keyword, limit 50 }) { let logs: any[] []; if (clientId) { logs logBuffer.get(clientId) || []; } else { for (const buf of logBuffer.values()) logs logs.concat(buf); } if (level) logs logs.filter(l l.level level); if (keyword) logs logs.filter(l JSON.stringify(l).includes(keyword)); logs logs.slice(-limit); return { content: [{ type: text, text: JSON.stringify(logs, null, 2) }] }; } ); const transport new StdioServerTransport(); await server.connect(transport);这里有个细节返回格式要用content数组。MCP 协议规定工具返回值必须是{ content: [...] }结构每个元素有type和text字段。我一开始直接返回数组AI 客户端解析不了。后来改成标准格式就正常了。另外text字段里我用JSON.stringify格式化了一下加了缩进AI 读起来更清晰。4.3 H5 页面接入与 vConsole 改造页面这边需要引入改造后的 vConsole。我把它打包成一个单独的 JS 文件通过script标签引入。初始化时传入 WebSocket 地址和clientId然后 vConsole 就会自动开始推送数据。script src./vconsole-mcp.js/script script new VConsoleMCP({ wsUrl: ws://127.0.0.1:8765, clientId: page- Date.now(), token: your-token-here }); /script改造 vConsole 的核心是重写console插件的printLog方法。原版方法只负责渲染我在后面追加了推送逻辑const originalPrintLog VConsole.prototype.printLog; VConsole.prototype.printLog function(level, args) { originalPrintLog.call(this, level, args); if (this.ws this.ws.readyState WebSocket.OPEN) { this.ws.send(JSON.stringify({ type: log, payload: { level, args: args.map(safeStringify), timestamp: Date.now() } })); } };网络请求的拦截类似重写XMLHttpRequest.prototype.open和send在onreadystatechange里推送请求详情。注意要保留原始方法不能影响页面正常请求。我试过直接替换XMLHttpRequest结果有些库不兼容后来改成只劫持open和send在回调里追加逻辑兼容性就好多了。4.4 MCP 客户端配置与联调以 Claude Desktop 为例配置文件在~/Library/Application Support/Claude/claude_desktop_config.jsonmacOS或%APPDATA%\Claude\claude_desktop_config.jsonWindows。在mcpServers字段里加上我们的服务{ mcpServers: { vconsole: { command: node, args: [/path/to/your/dist/server.js] } } }配置好后重启 Claude Desktop在对话框里输入“帮我看看页面有什么报错”AI 就会自动调用get_logs工具把 error 级别的日志拉出来分析。我实测下来从页面报错到 AI 给出分析整个过程不到 10 秒比手动复制日志快太多了。联调时有个小技巧先用clear_data清空缓存。因为缓冲区里可能残留之前的日志不清空的话 AI 会看到无关信息。我一般在开始新一轮调试前先让 AI 调用clear_data然后再操作页面复现问题这样日志最干净。5. 常见问题与排查技巧实录5.1 WebSocket 连接失败排查最常见的问题是 WebSocket 连不上。症状是页面控制台报WebSocket connection failedMCP Server 那边没有任何连接记录。排查思路分三步先确认服务是否启动用netstat -an | grep 8765看端口有没有监听再确认地址是否正确ws://127.0.0.1:8765和ws://localhost:8765在某些环境下行为不一样建议统一用127.0.0.1最后确认防火墙有没有拦截本地回环一般不会但有些安全软件会管。如果服务启动了、地址也对还是连不上那可能是端口被占用。换个端口试试比如 8766。我遇到过好几次端口冲突都是因为之前启动的服务没关干净。建议在服务启动时加个端口检测如果被占用就自动换一个或者直接报错提示。5.2 日志丢失或延迟的排查日志丢失通常有两个原因。一是缓冲区溢出日志太多把旧记录挤掉了。解决办法是调大缓冲区或者用clear_data及时清理。二是WebSocket 断线期间的数据没补推。检查重连逻辑里的补推队列是否正常工作可以在onopen回调里打印一下队列长度确认有没有积压。延迟问题一般是心跳间隔太长导致的。如果页面在后台被节流心跳可能延迟到几分钟才发一次服务端会误判为断线。我的做法是在visibilitychange事件里监听页面可见性页面回到前台时立即发一次心跳这样能快速恢复连接状态。5.3 AI 读不到日志的排查有时候 MCP Server 明明收到了数据但 AI 就是读不到。这种情况先检查工具调用是否成功。在 Claude Desktop 里工具调用会有个折叠面板展开能看到返回内容。如果返回空数组说明过滤条件太严了比如level设成了error但页面只有log。把条件放宽再试。另一个可能是clientId 不匹配。页面初始化时生成的clientId和 AI 查询时传的不一致就会查不到数据。建议在页面初始化时把clientId打印到控制台AI 查询时直接复制这个值。我后来干脆在 MCP Server 里加了个list_clients工具AI 可以先查有哪些页面在线再指定clientId查询省得手动对。5.4 常见问题速查表问题现象可能原因排查方法解决方案WebSocket 连接失败服务未启动/端口占用/地址错误检查端口监听、确认地址启动服务、换端口、用 127.0.0.1日志丢失缓冲区溢出/断线未补推查看缓冲区大小、检查补推队列调大缓冲区、修复重连逻辑日志延迟心跳间隔太长/页面后台节流检查心跳日志、监听可见性缩短心跳、页面回前台立即心跳AI 读不到日志过滤条件太严/clientId 不匹配放宽条件、核对 clientId调整参数、用 list_clients 查询页面刷新后数据残留旧连接未清理检查服务端连接 Mapbeforeunload 主动关闭连接5.5 几个实操心得第一个心得是日志分级要合理。不要把什么信息都往error级别塞否则 AI 查询时会被大量误报淹没。我的做法是真正的异常用error警告用warn普通信息用log调试细节用info。这样 AI 查error时就能快速定位到真正的问题。第二个心得是请求体要截断。有些接口的请求体特别大比如上传文件直接把整个 body 推过去会撑爆缓冲区。我的做法是只保留前 500 个字符超出部分用...[truncated]代替。响应体同理只保留前 1000 个字符。这样既能看清请求内容又不会因为数据太大影响性能。第三个心得是给 AI 加个上下文提示。在 MCP Server 的instructions字段里我写了一段说明告诉 AI 这个服务是用来调试 H5 页面的日志和请求分别代表什么查询时应该注意什么。这样 AI 在调用工具时会更准确不会问一些无关的问题。实测下来加了这段说明后AI 的分析质量明显提升。6. 扩展方向与个人体会这套东西跑通之后我又试了几个扩展方向。一个是把日志和请求关联起来比如某个请求失败时自动把前后 5 秒的日志一起返回这样 AI 能看到完整的上下文。另一个是加个截图功能页面报错时自动截个图通过 MCP 传给 AI让它能看到页面长什么样。截图用html2canvas实现虽然有点重但在排查样式问题时特别有用。还有个方向是多端聚合。现在只支持 H5但很多项目是 App H5 混合的。如果能通过某种方式把 App 原生日志也接进来AI 就能同时看到原生和 H5 的信息排查跨端问题会更方便。这个还在探索中主要难点是原生端的接入方式Android 和 iOS 各有各的坑。我个人在实际操作中的体会是这套方案最大的价值不是“让 AI 看日志”这个动作本身而是改变了 debug 的协作模式。以前排查问题前端要看日志、测试要复现、后端要查接口信息在几个人之间传来传去效率很低。现在 AI 直接读日志你只需要描述现象它就能给出可能的原因和排查方向。虽然 AI 不一定能直接定位到根因但它能帮你快速缩小范围省下大量翻日志的时间。最后再分享一个小技巧把 MCP Server 做成常驻服务。我一开始每次调试都手动启动后来用pm2或systemd把它做成后台服务开机自启这样随时都能用。配合clear_data工具每次调试前清一下缓存体验很流畅。如果你经常调试 H5这套东西值得花半天时间搭起来后面省下的时间绝对不止半天。