☰
vConsole MCP:让AI直接看见H5运行时数据的调试方案
2026/10/26 16:44:16 网站建设 项目流程

调试H5最烦的不是代码难写,而是"看不见"。在电脑上打开DevTools,console、Network、Sources随便看,一切尽在掌握;同样的页面一旦跑到真机上,尤其放进小程序WebView或App内置WebView,想看个报错就不是"按F12"这么简单了。更多人会线上注入vConsole,在页面上拉出一个悬浮调试面板——这确实解决了"看见日志"的问题,但新的麻烦又来了:日志刷得飞快、请求要一条条翻、数据还要自己分析。vConsole MCP想解决的就是这件事:把vConsole收集到的H5日志和网络请求,通过MCP协议暴露给AI,让AI直接看见页面运行时的现场数据,然后帮你debug。

这篇文章会把整个方案的架构、核心实现、实测过程和踩过的坑完整写出来。适合正在折腾H5调试、或者想在AI辅助开发里打通"运行时数据"这条链路的人参考——不管你是前端工程师、全栈开发,还是做低代码平台的,思路都可以直接复用。

1. 为什么AI调试H5时缺的不是智能,而是"眼睛"

1.1 真机H5调试:一场漫长的"盲人摸象"

桌面端开发H5是舒服的,DevTools里Console、Network、Application面板齐全,断点、watch、Performance随便用。但同一套代码跑到真机上,所有便利都会打折扣。手机浏览器没有F12,App内嵌WebView更是连地址栏都看不到,调试工具基本靠外围手段补。

常见的做法无非几种:vConsole/eruda这类浮层面板、Android下的chrome://inspect、iOS Safari的Web Inspector、还有weinre那类老古董。chrome://inspect和Web Inspector本质上还是远程DevTools,需要插线、配端口、开开发者模式,链路又长又脆,很多人试一次就放弃了。vConsole因为直接注入页面、扫码就能看,成了大部分团队的首选。

但vConsole有个本质局限:它的面板是给"人"看的。日志、请求都躺在页面里,你需要自己滚、自己找、自己分析。如果日志量很大,或者你根本不在现场(比如线上用户报了一个bug),这个面板就帮不上什么忙了。真正难debug的H5问题,往往发生在你不在场的时候。

1.2 AI编程助手的盲区:它只能读静态代码

这两年AI编程工具进步很快,Cursor、Claude Code这些在IDE里读源码、写函数、生成单测都很强。但如果你让AI去定位一个"真机上报错"的问题,它其实非常吃力。原因是:AI能看到你的仓库代码,却看不到页面运行时的状态。

比如用户反馈"点了提交按钮没反应",你在IDE里问AI为什么,它只能从代码逻辑上猜测:也许是事件没绑上、也许接口没调、也许是某个变量为空。但真正的原因往往藏在运行时数据里——某条console.error、某个接口返回了500、某个对象在渲染时是undefined。这些信息,IDE里的AI是拿不到的。

过去我们的工作流是:用户截图报错->自己在vConsole里翻日志->把关键信息复制给AI->AI给出建议。整个过程有大量人工搬运,而且经常出现"你复制的日志刚好漏掉了最关键的那条"。我自己就吃过这个亏:有一次查一个白屏问题,翻了半小时Network,最后发现是一个字体文件被WAF拦了,而这条请求在vConsole面板里早就被滚过去了。

1.3 MCP恰好补上了"运行时数据"这条链路

MCP(Model Context Protocol)是过去一年里AI工具链里最重要的协议之一,它做了一件事:把AI模型和外部数据源之间的连接标准化。你可以把它理解成给AI配了一堆USB接口——原来AI只能"看着"你贴在屏幕上的代码,现在通过MCP这个USB口,它可以连接你的数据库、终端、浏览器调试面板,直接读取实时数据。

MCP的模型很简单:服务端通过tools暴露能力,AI客户端在对话过程中自主决定调用哪个工具、传什么参数,服务端把数据返回给AI。整个过程中,AI不再是"猜",而是"查"。

vConsole MCP的思路就是这样:把vConsole在页面里捕获的日志、网络请求、错误事件,通过一个本地服务包装成MCP工具。AI接到你的问题后,可以先调用get_logs看报错,再调用get_network_requests查请求状态,自己完成"看面板"这一步,然后直接给出排查结论和修复建议。人从"翻面板"变成"审结论"。

2. 架构设计:页面、桥、协议三层链路怎么分工

2.1 两条路线的取舍:页面主动推送还是AI主动拉取

我最开始想过一个更简单的做法:让页面把日志暴露成一个HTTP接口,比如GET http://localhost:9222/logs,AI的工具直接去轮询这个接口。这样链路最短,代码也最少。但实际一跑就发现问题了。

如果H5跑在真机上,页面地址是http://192.168.x.x:8080这类局域网地址,本地服务也要开着对应端口供页面访问,网络环境一复杂就联不通。如果页面本身是HTTPS的,浏览器还会拦截对http://localhost:9222的请求,这就是mixed content问题,日志根本推不出去。AI去拉取的方式还有一个致命的时序问题:AI并不知道页面什么时候产生了新日志,只能无脑轮询,效率低不说,还会在页面上留下大量请求噪音。

最终我采用的是推送为主的方案,AI侧全部走MCP工具调用来完成,页面不再主动暴露拉取接口。两条路线的对比可以看这张表:

对比项页面Push(WebSocket推送)AI Pull(HTTP轮询)
真机适配页面主动连本地服务,反向打通,适配性好本地服务去连页面,跨域和混合内容问题多
实时性日志产生即入队,AI随时可查轮询频率决定延迟,做高了又打扰页面
对页面性能影响低,只推数据不阻塞中,每次轮询都有网络栈开销
实现复杂度中,需要维护WebSocket链路低,普通HTTP接口即可
数据完整性断线有重传机制,日志不易丢轮询间隙的日志容易错过

实际工程里,纯Push也不是万能的,我会在后面"踩坑"部分讲HTTPS页面连不上WebSocket时的兜底方案。至少在这个项目的第一版,Push路线是性价比最高的。

2.2 三层链路:注入插件、Debug Bridge、MCP Server

整个系统拆成三层,每层职责单一,调试起来也清爽:

  • H5页面内的注入脚本:负责捕获console、fetch、XHR、window.onerror、unhandledrejection,把原始数据整理成结构化的log和request事件,通过WebSocket推给本地桥服务。它只做采集和推送,不做任何分析。
  • 本地Debug Bridge:一个Node进程,跑9222端口,负责接收页面推来的数据,存进内存环形队列。它相当于一个"数据中转站",把浏览器端的事件流缓存起来供AI查询。
  • MCP Server:实现MCP协议的服务端,通过get_logs、get_network_requests等工具,把Bridge里的数据暴露给AI客户端。AI侧看到的是一组"像查数据库一样方便"的调试工具。

链路整体是这样的:H5页面 -> WebSocket推流 -> Debug Bridge(内存队列) -> MCP Server(tools) -> AI客户端。

为什么中间要单独拆一个Bridge,而不是让页面直接连MCP Server?因为MCP Server最常见的运行形态是挂在本机IDE或编辑器里的stdio通道中,浏览器端的JavaScript根本没法直接跟stdio对话。拆出Bridge后,页面只需要知道"把数据推到一个WebSocket端口",MCP Server只需要知道"从内存队列里取数据",两边解耦,将来想换成HTTP上报、或者把Bridge部署到内网服务器,都不用动另一侧。

2.3 数据契约:日志和请求必须结构化

AI能高效利用数据的前提,是数据本身足够结构化。如果只把vConsole里那种纯文本String倒给AI,它还是得自己解析,容易出错。我给日志和请求各定义了一个统一格式。

日志事件:

字段类型说明
idnumber递增序号,方便AI引用
typestring固定为log
levelstringdebug/log/info/warn/error,对应console级别
messagestring日志内容,已经序列化为字符串
timestringISO 8601格式时间戳
pageUrlstring产生日志的页面URL,SPA多路由时很有用

请求事件:

字段类型说明
idnumber递增序号
typestring固定为request
methodstringGET/POST/PUT/DELETE等
urlstring请求地址,可能是相对路径或完整URL
statusnumberHTTP状态码,没完成时为0
durationnumber请求耗时,毫秒
requestBodystringPOST等请求体,做截断
responseBodystring响应体,超过2KB截断
timestring请求发起时间

这个结构看起来朴素,但实际用的时候作用很大。AI拿到请求记录后,可以直接按状态码过滤、按URL关键字搜索、按耗时排序,这些操作用纯文本日志很难做。实测下来,结构化的数据比"把整个vConsole面板截图给AI"要可靠得多。

3. 核心实现:从劫持console到注册MCP工具

3.1 页面注入脚本:先于vConsole捕获,再做可视化

一个关键的设计决定是:注入脚本要在vConsole初始化之前执行,自己完成数据捕获,而不是等着从vConsole内部掏数据。原因很简单,vConsole的插件机制主要用于扩展UI面板,它的内部日志和请求数据并没有提供稳定的公开查询API;而且在线上环境我未必想开vConsole,但日志采集是必须一直开的。

所以采集器写在页面最早执行的脚本里,vConsole只作为可选的视觉面板。核心代码大致是这样的:

// vconsole-mcp-bridge.js (function () { if (window.__VConsoleMcpBridge__) return; window.__VConsoleMcpBridge__ = {}; const MAX_BUFFER = 500; const pending = []; // 劫持 console const originalConsole = window.console; ['debug', 'log', 'info', 'warn', 'error'].forEach((level) => { const originFn = originalConsole[level].bind(originalConsole); originalConsole[level] = function (...args) { const message = args.map((arg) => { try { return typeof arg === 'object' ? JSON.stringify(arg) : String(arg); } catch (e) { return String(arg); } }).join(' '); pending.push({ type: 'log', level, message, time: new Date().toISOString(), pageUrl: location.href }); if (pending.length > MAX_BUFFER) pending.shift(); originFn(...args); }; }); // 劫持 window.onerror 和 unhandledrejection window.addEventListener('error', (e) => { pending.push({ type: 'log', level: 'error', message: `Uncaught Error: ${e.message} @ ${e.filename}:${e.lineno}:${e.colno}`, time: new Date().toISOString(), pageUrl: location.href }); }, true); window.addEventListener('unhandledrejection', (e) => { pending.push({ type: 'log', level: 'error', message: `Unhandled Rejection: ${e.reason}`, time: new Date().toISOString(), pageUrl: location.href }); }); // WebSocket 推送逻辑 function connect() { const protocol = location.protocol === 'https:' ? 'wss' : 'ws'; const ws = new WebSocket(`${protocol}://${location.hostname}:9222`); ws.onopen = () => { window.__VConsoleMcpBridge__.flush = () => { if (!pending.length) return; ws.send(JSON.stringify({ batch: pending.splice(0, pending.length) })); }; // 立即推送一次存量日志 window.__VConsoleMcpBridge__.flush(); }; ws.onclose = () => { window.__VConsoleMcpBridge__.flush = null; setTimeout(connect, 3000); }; } connect(); })();

这段代码有几个细节值得注意。message统一序列化成字符串,是为了避免AI拿到[object Object]这种没用的东西。MAX_BUFFER限制在500条,防止高频循环日志把内存打爆。window.addEventListener('error', ..., true)用捕获阶段,能捞住更多运行时错误,这个不加true会漏掉不少子iframe和Shadow DOM里的异常。

3.2 网络请求捕获:fetch和XHR都要处理,且必须"看过就还回去"

网络请求捕获比console要麻烦,核心问题在于响应体只能读一次。如果用fetch的resp.text()读完,页面自己的代码再读响应就是空的了。所以这里必须用clone():

// 劫持 fetch const originalFetch = window.fetch; window.fetch = async function (...args) { const url = typeof args[0] === 'string' ? args[0] : (args[0] && args[0].url || ''); const method = (args[1] && args[1].method || (typeof args[0] === 'string' ? 'GET' : 'GET')).toUpperCase(); const startTime = Date.now(); try { const resp = await originalFetch.apply(this, args); const cloned = resp.clone(); cloned.text().then((body) => { pushRequest({ type: 'request', method, url, status: resp.status, duration: Date.now() - startTime, responseBody: body.length > 2000 ? body.slice(0, 2000) + '...[truncated]' : body }); }).catch(() => {}); return resp; } catch (err) { pushRequest({ type: 'request', method, url, status: 0, duration: Date.now() - startTime, responseBody: `FetchError: ${err}` }); throw err; } }; // 劫持 XHR const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url) { this.__mcpMethod = method; this.__mcpUrl = url; this.__mcpStart = Date.now(); return originalOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function (body) { this.addEventListener('load', function () { const respBody = this.responseText || ''; pushRequest({ type: 'request', method: this.__mcpMethod, url: this.__mcpUrl, status: this.status, duration: Date.now() - this.__mcpStart, requestBody: typeof body === 'string' ? body.slice(0, 2000) : '', responseBody: respBody.length > 2000 ? respBody.slice(0, 2000) + '...[truncated]' : respBody }); }); this.addEventListener('error', function () { pushRequest({ type: 'request', method: this.__mcpMethod, url: this.__mcpUrl, status: 0, duration: Date.now() - this.__mcpStart, responseBody: 'NetworkError' }); }); return originalSend.apply(this, arguments); };

响应体截断到2KB是实测后的选择。很多后端接口的响应体动辄几十KB,全量塞进内存队列会让Bridge很快爆掉,而且AI上下文窗口也吃不下。2KB对于绝大多数问题排查足够,如果确实需要看完整响应,我在Bridge里留了一个get_full_response(requestId)工具可以单独拉取,避免污染主队列。

另外要特别强调一下:捕获XHR的load事件时,不要在send的同步代码里读取responseText,因为请求还没结束。同时要注意open里存的自定义属性方式比较粗暴,如果页面自己也有类似的扩展逻辑可能会冲突,但实测下来概率极低,可以接受。

3.3 Debug Bridge:内存环形队列就够了

Bridge的代码非常简单,核心就是一个WebSocket服务和两个环形队列。日志上限1000条、请求上限1000条,超过的自动丢弃最老的数据。

// debug-bridge/index.js const { WebSocketServer, WebSocket } = require('ws'); const wss = new WebSocketServer({ port: 9222 }); const queue = { logs: [], requests: [], max: 1000 }; function pushLog(item) { queue.logs.push(item); if (queue.logs.length > queue.max) queue.logs.splice(0, queue.logs.length - queue.max); } function pushRequest(item) { queue.requests.push(item); if (queue.requests.length > queue.max) queue.requests.splice(0, queue.requests.length - queue.max); } wss.on('connection', (ws) => { ws.on('message', (raw) => { try { const msg = JSON.parse(raw.toString()); if (!msg.batch || !Array.isArray(msg.batch)) return; msg.batch.forEach((item) => { if (item.type === 'log') pushLog(item); else if (item.type === 'request') pushRequest(item); }); } catch (e) { // 忽略非法消息 } }); }); module.exports = { queue };

注意环形队列的splice(0, length - max)这个写法,直接在原数组上裁剪,比shift()循环高效得多。日志量大的时候能明显感觉到差别。Bridge还可以顺手把数据打点输出到本地文件,方便事后回溯,这个看个人需要。

3.4 MCP Server:工具描述是AI用的"说明书"

MCP Server我用的官方TypeScript SDK,注册工具时最重要的一环不是实现逻辑,而是把description和参数Schema写清楚。AI是根据工具的描述来决定"这个场景该不该调它"的,描述写得太笼统,AI就不知道该在什么时候用,甚至干脆不调用。

// mcp-server/index.ts import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import { queue } from '../debug-bridge'; const server = new McpServer({ name: 'vconsole-mcp', version: '1.0.0' }); server.tool( 'get_logs', '读取H5页面捕获的console日志。当你怀疑页面有报错、或者想了解页面运行时的警告信息时,先调用这个工具。可以按level过滤。', { level: z.string().optional().describe('日志级别:debug/log/info/warn/error,不传返回全部'), limit: z.number().min(1).max(200).optional().describe('返回条数,默认50,按时间新到旧') }, async ({ level, limit = 50 }) => { const logs = queue.logs .filter((item) => !level || item.level === level) .slice(-limit); return { content: [{ type: 'text', text: JSON.stringify(logs, null, 2) }] }; } ); server.tool( 'get_network_requests', '读取H5页面捕获的网络请求记录。当页面出现接口报错、请求失败、数据渲染不出来等问题时,调用这个工具。可按状态码和URL关键字过滤。', { status: z.number().int().optional().describe('HTTP状态码,如500、404,不传返回全部'), urlKeyword: z.string().optional().describe('URL关键字,模糊匹配'), limit: z.number().min(1).max(200).optional().describe('返回条数,默认50') }, async ({ status, urlKeyword, limit = 50 }) => { const requests = queue.requests .filter((item) => (status === undefined || item.status === status) && (!urlKeyword || item.url.includes(urlKeyword))) .slice(-limit); return { content: [{ type: 'text', text: JSON.stringify(requests, null, 2) }] }; } ); const transport = new StdioServerTransport(); await server.connect(transport);

这里有个很实用的经验:工具描述里直接写"什么时候应该调用"。比如get_logs的描述里加一句"当你怀疑页面有报错时,先调用这个工具",AI就会在用户说"页面白屏了""按钮没反应"这类模糊问题时优先查日志,而不是瞎猜。工具命名也尽量用动词加名词,get_logs、get_network_requests这种AI一眼就能理解语义的名字,要好过query_data这样的抽象命名。

4. 实测记录:AI从"猜原因"到"看现场"的完整过程

4.1 接入配置清单

整个项目跑通需要的步骤不多,我整理成一份保姆级清单:

  1. 把注入脚本放到H5项目的公共JS里,建议在构建配置里加环境判断,只有import.meta.env.DEV === true或者NODE_ENV === 'development'时才加载。
  2. 启动本地服务:node debug-bridge/index.js & npx tsx mcp-server/index.ts,Bridge监听9222端口,MCP Server通过stdio暴露。
  3. 在AI客户端里连接MCP Server。如果你用的是支持MCP的编辑器或AI工具(Claude、Cline等以MCP为标准的客户端),配置里添加一条command: npx tsx mcp-server/index.ts的记录即可。
  4. 手机和电脑连同一个WiFi,在手机上打开H5页面。如果页面跑在电脑浏览器,直接用localhost访问也可以。
  5. 打开页面后,看到Bridge打印出连接成功的日志,就说明链路已经通了。此时可以在页面上随便点几个按钮、刷几条请求,让AI去查。

4.2 一次真实Bug:AI自己查日志、翻请求、定位根因

我拿一个最近实测的Bug场景来说。页面是一个商品下单页,用户点"提交订单"按钮后一直loading,最后弹"创建失败"。

我把这个问题交给接了vConsole MCP的AI,只说了六个字:"下单按钮报错"。AI的排障过程是这样展开的:

AI:先看一下页面日志里有没有error级别的报错。 调用 get_logs(level: "error", limit: 50) 返回: [ {"level":"error","message":"Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'map')","time":"2025-06-11T14:32:01.235Z","pageUrl":"https://m.example.com/order/123"}, {"level":"error","message":"POST /api/order/preview 500","time":"2025-06-11T14:32:01.210Z"} ] AI:有两条错误日志,一条是JS异常,一条是接口500。再看一下这个失败请求的详细信息。 调用 get_network_requests(status: 500, limit: 20) 返回: [ {"method":"POST","url":"/api/order/preview","status":500,"duration":342, "requestBody":"{\"skuIds\":[\"1244\",\"775\"],\"activityId\":\"888\"}", "responseBody":"{\"code\":\"SKU_STOCK_MISSING\",\"message\":\"商品sku不存在\"}"}, ] AI:找到原因了。前端在点击提交时先调 /api/order/preview 做预下单,后端返回 500 且 code 为 SKU_STOCK_MISSING。前端拿到失败响应后,代码里对 resp.data.skus 直接执行了 .map,而失败响应里没有 skus 字段,引发 TypeError 导致流程中断,只弹了"创建失败"。建议:在请求成功回调里先判空 skus,再执行 map;同时对非200响应统一走错误分支。

整个过程里我没有手动去vConsole面板翻任何一条记录,AI自己完成了"查日志、查请求、关联上下文、定位根因"这四步。相比以前自己翻面板、复制粘贴给AI的流程,节省至少十分钟,而且不会漏信息。

4.3 上下文裁剪与数据脱敏:给AI的"食物"要干净

实测中我很快发现一个细节:日志和请求数据不是越多越好,给多了反而干扰AI判断,甚至挤占对话上下文窗口。

我建议默认配置是:日志limit=50,请求limit=50,按需扩大。同时MCP Server在返回数据时做一层脱敏,把Cookie、Authorization、token字段统一替换成***。理由是AI侧不一定有处理敏感数据的安全边界,尤其当你在用云端的AI服务时,请求体里的用户信息属于隐私数据,不该被当作文本发给外部模型。

脱敏代码我放在Bridge推送入口处,而不是MCP Server侧——因为越早在链路前段做,越能保证后面所有环节都拿不到裸数据。这个习惯后来帮了我一次:有一次排查支付问题,请求体里居然带着用户的手机号,如果没有提前脱敏,这种数据就被原样发到AI侧了。

5. 踩过的坑和边界条件

5.1 HTTPS页面上WebSocket连不上:Mixed Content问题

这是我在真机联调时遇到的第一个大坑。完全按demo跑,电脑浏览器http://localhost:3000没问题,一换成线上HTTPS环境,日志一条都推不过来。原因是浏览器安全策略:HTTPS页面禁止发起ws://连接,只能连wss://。

我在注入脚本里做了个兼容:如果页面是HTTPS,自动切换成HTTP POST批量上报模式,Bridge端再加一个POST /report接口作为兜底,页面每两秒批量POST一次数据。这样链路虽然不如WebSocket实时,但至少能跑通。另一种方案是给本地Bridge生成自签名证书跑wss://,但在手机端要进行证书信任操作,略繁琐,我把这个做成可选。优先推荐HTTP POST兜底,因为只要Bridge监听端口,手机在同一局域网就能上报,不需要额外处理证书。

5.2 SPA路由切换导致连接断开、日志丢失

SPA项目基本都用hash或history路由。hash路由在刷新页面时整个应用重新加载,WebSocket会断开重连,之前队列里没推送的数据会丢。history路由因为不刷新页面,连接是稳定的,但会产生路由变化时日志归属混乱的问题。

我在注入脚本里给history.pushState和history.replaceState做了包装,每次路由变化时主动推一条type: 'routeChange'的日志,这样AI查日志时能清楚看到"从哪个页面跳到了哪个页面"。实测对排查"白屏出现在路由切换后"这类问题非常有用。

日志风暴也要处理:某些页面会在循环或高频事件里疯狂打印日志,一秒能刷几十上百条。1000条队列很快被洗掉,真正关键的error反而被挤出去了。我在push逻辑里加了两条规则:一是同一level的连续日志如果超过20条,合并为一条similar log x20,二是error级别日志不做合并,永远保留。这样既控制了总量,又保住了重要的错误线索。

5.3 生产环境必须默认关闭,安全意识要拉满

这个项目本质是在采集页面运行时数据,采集能力一旦上线,本身就是一种暴露。我在接入清单里强调了:注入脚本只应在开发或预发环境加载,生产构建必须通过环境变量硬关闭。判断方式是构建时注入一个__VCONSOLE_MCP_ENABLED__的全局常量,而不是运行时判断location.hostname,因为运行时判断可以被绕过。

安全核对清单基本是这几项:

检查项要求
构建开关__VCONSOLE_MCP_ENABLED__只在非生产环境为true
Bridge监听范围只绑127.0.0.1,不要绑0.0.0.0(真机联调时再临时允许局域网)
脱敏字段Cookie、Authorization、token、手机号、身份证号强制替换
队列上限日志和请求各1000条,防止内存膨胀
工具权限MCP Server只提供读取和清空,不提供向页面写代码的能力

实测中我建议真机联调结束马上把Bridge关掉,不要一直挂着,省得其他人扫端口扫到一个开放调试口。这不是杞人忧天,我见过有团队把类似调试服务直接暴露到公网IP上,结果被安全扫描器标记的案例。

5.4 哪些H5场景不适合这套方案

要诚实地说,vConsole MCP不是万能调试器。有三个场景我实测下来效果不佳:

第一类是WASM内部调用。如果页面核心逻辑跑在WebAssembly里,JS层的console和fetch捕获根本看不到WASM模块内部的状态,AI拿到的是黑盒外面的日志。这种情况还是需要专门的WASM调试手段。

第二类是加密或二进制协议。比如WebSocket里直接传Protobuf、MessagePack这类二进制格式,我的注入脚本只捕获了WebSocket的发送/接收事件,但不会帮你解析二进制内容。除非你预先知道协议格式并在采集器里做解码,否则AI看到的就是一堆乱码。

第三类是原生App直接加载的远程H5,且WebView配置禁用了混合内容。我在项目里遇到过一次:某个银行类App为了让H5更安全,setMixedContentMode设成了MIXED_CONTENT_NEVER_ALLOW,这种情况下任何本地桥的连接都进不来,只能改在页面内部用浮动面板自查。这类场景的安全性约束是合理的,不能为了调试方便去突破它。

写在最后

做完这个项目给我最大的感受是:AI辅助调试的价值,不在于"替你写代码",而在于把调试现场的数据完整地喂给它。以前我们调试H5,是"人对着面板找线索";现在可以变成"AI对着一堆结构化现场数据找线索,人只看AI给的结论"。这种工作流的转变,在那些"低级但耗时"的bug上效率提升特别明显——接口字段拼写错、对象空指针、请求时序混乱,这些问题AI处理起来又快又准。

最后分享一条个人经验:给MCP工具写description时不要惜字如金,把你平时看Console面板的第一反应写进去。比如"查网络错误先用这个工具"、"页面报错时先看error日志"这类引导,AI真的会按照你的排查习惯去工作。工具本身是死的,工作流是活的。这套vConsole MCP的路子,未来如果再扩展一个截图能力,AI就不止能看日志和请求,还能直接"看"到页面长什么样,那又是另一个维度的调试了。先把数据链路搭好,后面的一切都好说。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询