
平时做大模型应用开发的人应该都有个共同的痛点——想让AI替我操作网页光是把工具定义清楚就得耗掉半天要写MCP server要给每个操作写JSON Schema要一遍遍调试参数传得对不对。前阵子我被这种事折磨得不轻索性做了个小工具可以录制任意网页操作然后一键转成能被大模型直接调用的MCP Server。这个工具的核心价值就一句话把“录屏”变成“MCP工具”。简单说你不用再手写工具描述、不用再为某个按钮写定位器只要在浏览器里点一点、填一填就能得到一个标准MCP Server。想让你自己的大模型客户端操作某个网站、处理某个固定流程直接用这个思路就行。这篇文章会把背后的设计思路、录制转MCP的核心逻辑、完整实操过程以及我踩过的坑都说清楚。1. MCP到底解决了什么问题为什么需要“录制转MCP”1.1 MCP是AI应用的“USB接口”在讲这个工具之前先得把MCP协议讲明白。MCPModel Context Protocol本质上是给大模型和外部工具之间做的标准化接口。你可以把它理解成大模型世界的USB接口——以前每个外部系统都要单独定制连接方式现在大家统一用MCP协议描述“我这个工具叫什么、有什么参数、能干什么”大模型只要理解了协议就能直接调用。一个MCP server通常会暴露一组tools每个tool包含三部分工具名称、功能描述、参数Schema。大模型读到这些内容后会根据用户指令自动判断“该用哪个工具、传入什么参数”。这就是为什么最近无论是Claude Desktop、还是各类AI编程工具都在大力支持MCP——它在把AI从“只会聊天”推向“真正能干活”。但这个统一接口有个现实问题写MCP server的人很痛苦。因为一个简单的“在页面上输入订单号然后点击查询”你得写清楚工具描述、定义输入参数、写一段执行逻辑去操控浏览器。这是一个非常琐碎且重复的工作。1.2 传统写MCP工具的逻辑有多累我之前试过手动写一个MCP server去控制某个报表系统就一个查询动作过程是这样的先新建项目、安装MCP SDK然后定义一个输入参数orderId再写一段Playwright代码去打开页面、等待输入框出现、输入内容、点击按钮、等待结果渲染。整个流程下来大部分时间不是在写有挑战性的逻辑而是在重复描述“第几步做什么”。更难受的是如果这个页面有两个类似的流程就得再来一遍。我当时就想录制浏览器操作的方案已经存在十几年了为什么不把录制结果直接变成MCP工具录下来的每一步操作天然就是一个工具的执行体操作过程中的输入项天然就是工具的入参。这才是把网页操作和大模型能力打通的最短路径。2. 工具核心拆解从“录屏”到“可调用的Tools”2.1 录制层的设计采集意图不采集血泪录制网页操作听起来像录屏但实际技术实现上完全不是一回事。真正的网页操作录制采集的不是视频像素而是结构化事件数据。每个操作都对应一个Q: 点击了哪个按钮、往哪个输入框填了什么、页面跳转到了哪里。我做的录制方案选择了浏览器扩展内容脚本注入在页面加载时注入一段JavaScript脚本监听用户的实际DOM事件。业界常用的录制事件包括click、input、scroll、navigate、select等。每个事件记录的关键字段有选择器、操作类型、输入值、触发时间、等待时长等。这类方案的优点是录制的操作直接绑定在真实DOM节点上执行时可靠度比较高。相比截屏式录制只能靠图像识别猜测点击位置结构化录制能拿到精确的定位信息。尤其对于大模型工具调用来说结构化数据更友好——参数可以传、步骤可以调、结果可以查。2.2 操作的抽象与参数标记光有原始操作记录还不够必须把它抽象成“工具可执行的步骤”。我设计了一个中间表示层把录制到的原始事件规整成统一的操作序列形成了下面的格式{ version: 1, name: query_order_and_export, description: 查询指定订单并导出报表, steps: [ { id: 1, type: goto, url: https://report.example.com/orders, wait_after: 300 }, { id: 2, type: input, selector: #order_id_input, value: {{orderId}}, wait_after: 300 }, { id: 3, type: click, selector: button[data-testidquery_btn], wait_after: 1000 }, { id: 4, type: click, selector: button:has-text(导出), wait_after: 2000 } ], parameters: [ { name: orderId, type: string, description: 要查询的订单号, match_selector: #order_id_input } ] }这里最关键的设计是“动态参数标记”。录制的时候你输入的是具体测试值比如订单号20240001。但工具运行时这个订单号应该来自大模型根据用户问题自动生成。所以在录制面板里用户可以把某个输入框标记为动态参数工具会把参数值替换到对应步骤的value字段中。这样每步操作都是模板具体执行时根据入参渲染。2.3 执行层为什么是Playwright而不是裸CDP录制完成只是第一步真正要生成MCP server时执行层选型也很关键。我最终选择了Playwright而不是直接用Chrome DevTools ProtocolCDP或原生Selenium原因有三点一是Playwright对选择器定位有非常完善的等待机制比如element.waitFor()可以在元素出现前保持等待避免页面异步加载时直接点击失败。二是它原生支持多浏览器录制工具如果只支持某个浏览器内核使用场景会窄很多。三是它的Context隔离做得不错可以方便地复用登录态、设置持久化Cookie这对于需要认证的网页操作非常重要。生成MCP server时我会把上面的JSON操作序列直接编译成可执行的Playwright脚本。每步骤映射成对应的操作调用参数通过插值方式动态注入。执行函数的外壳由MCP SDK接管这样生成的server在结构上是标准的能直接接入任何支持MCP的客户端。2.4 回放稳定性的细节工具要真正好用不是录出来就算了回放时的稳定性非常考验细节。最大的问题是“过快执行”录制时的人工操作一步可能间隔好几秒但回放时自动化跑得飞快页面跳转、数据渲染根本来不及。timeout策略是等元素可操作而不是机械等待固定毫秒数。为每个点击操作添加智能等待Playwright等待元素绑定的第一步执行完毕再继续下一步。下面代码展示了一个典型步骤的执行逻辑async function executeStep(step: Step, context: BrowserContext) { if (step.type click) { const locator page.locator(step.selector); await locator.waitFor({ state: visible, timeout: 15000 }); await locator.click(); } if (step.type input) { const locator page.locator(step.selector); await locator.waitFor({ state: visible, timeout: 15000 }); await locator.fill(renderTemplate(step.value, context.params)); } }配合wait_after字段作为兜底确保JS渲染型页面有足够的反应时间。这套组合拳让我在实际使用中回放成功率从最早的六成提升到九成五以上。3. 实操过程把一个报表查询流程变成MCP工具3.1 场景准备与关键选择拿一个非常典型的场景举例某内部报表系统需要查询指定订单、导出数据。这个场景有三个特点非常适合录制转MCP一是操作路径固定查完就导出不需要复杂决策二是有输入参数订单号每次不同适合作为MCP工具入参三是页面交互较复杂有表格渲染、弹窗确认等环节能验证工具的稳定性。在正式开始录制前我建议先做一次“预演”。因为录制过程中如果操作失误整个序列可能作废预演能帮你确认入口URL、按钮位置、页面加载时长。特别是那些有权限控制的系统预演时先手动登录一次确认有权限访问目标页面。3.2 录制过程实录实际录制流程如下打开报表系统首页点击扩展图标选择“开始录制”。输入一个测试订单号我用的是20240001点击“查询”按钮等待表格刷出来再点击“导出”按钮界面出现“导出成功”提示。最后停止录制。录制过程高度自动化我在界面上操作什么系统就记录什么。录制完成后在编辑面板里能直观看到每一步的缩略信息第1步前往首页第2步向#order_id_input输入20240001第3步点击查询按钮第4步点击导出。这时把第2步的输入框勾选为“动态参数”命名orderId。系统会自动把这个参数加入Parameters列表后续生成工具描述时直接映射为输入Schema。3.3 生成MCP Server并把它跑起来点击“生成MCP Server”工具会生成一个标准的TypeScript项目核心代码是这样的import { McpServer } from modelcontextprotocol/sdk/server/mcp.js; const server new McpServer({ name: report-ops, version: 1.0.0 }); server.registerTool( query_order_and_export, { description: 查询指定订单并导出报表, inputSchema: { type: object, properties: { orderId: { type: string, description: 要查询的订单号 } }, required: [orderId] } }, async ({ orderId }) { return runPlaywrightSteps(query_order_and_export, { orderId }); } );跑起来只需要两步npm install npx tsx ./src/server.tsMCP server跑起来后把它配置到客户端。以Claude Desktop为例修改配置文件加上如下内容{ mcpServers: { report-ops: { command: npx, args: [tsx, /path/to/your/project/src/server.ts] } } }重启客户端后能自动发现工具。3.4 在客户端实际调用配置完成后直接在对话中告诉大模型“帮我查一下订单12345678并导出。”大模型会基于工具描述自动匹配到query_order_and_export工具将订单号12345678作为orderId参数传入。随后server拉起无头浏览器执行录制好的Playwright操作序列最终把执行结果返回给大模型。整个过程对大模型来说就是“调了个函数”但对用户来说这个函数内部完成了一段真实网页操作。我在实际测试中用的不是无头模式而是headed模式这样能让用户看到浏览器弹出、输入、点击的过程。对于偏重感知的流程能直观看到工具执行过程会极大增强可信度。4. 常见问题与排查技巧实录4.1 选择器不稳定页面一改版工具就废了这是录制类工具最常遇到的问题。页面元素变了录制时保存的CSS选择器就定位不到了。解决思路是录制时不要只保存单一选择器而是保存多个候选。比如点击按钮既记录button[data-testidquery_btn]也记录相对位置和文本内容。执行时优先用有明确语义的如果定位失败再尝试文本匹配。文本定位对页面样式变动有很强的鲁棒性只要文案不变就能正常工作。实际操作中遇到最多的场景是前端加了新按钮导致选择器命中多个元素。我给录制系统加了重要性扫描记录元素可见性、唯一性权重只有唯一命中的选择器才作为首选。这个细节直接决定了回放成功率值得重视。4.2 动态参数替换失败大模型传进去的值不对动态参数替换的坑出现在一个场景如果某个输入框录制时值被标记为动态参数但执行时传入值的格式和页面要求不一致比如订单号应该是字符串页面可能只接受数字开头。这个问题我在实际使用中也遇到过一度怀疑是大模型传参错误后来排查发现是页面输入框的校验逻辑对“聚焦后回车”比较敏感单纯fill值不够。最终解决的方案是在参数Schema里增加格式校验动态参数支持正则约束。录制时还可以标记字段是文本、数字还是邮箱。MCP server在运行时先做一次本地校验不符合格式的直接返回错误信息不启动浏览器避免白白等超时。4.3 登录态复用与安全边界网页操作工具最现实的问题是要登录。每次调用工具都重新登录一遍不仅慢而且很多网站有验证码或风控。我用的是持久化Context方案录制时启用Persistent Context登录一次后把Cookie和StorageState保存下来之后每次回放都直接加载。大模型客户端配置里不需要传任何账号密码凭据只保存在本地项目中。需要提醒的是安全边界要把握好MCP server作为本地服务只能绑定127.0.0.1绝不能开放到公网。如果某天你需要远程调用建议通过内网隧道或加密通道转发而不是直接暴露端口。涉及支付、后台管理等敏感操作更要想清楚操作可回滚性。4.4 它和Browser Use MCP、Playwright MCP有什么不同聊到MCP操作网页很多人会提到另外两个方案。我把它们做了个对比方案核心定位适用场景主要不足Browser Use MCP大模型自主浏览网页自己决定每一步操作开放式探索、无固定流程任务决策依赖大模型推理速度慢、不稳定Playwright MCP大模型通过自然语言直接控制浏览器临时性调试、探索某个页面每个操作都要实时推理无法固化成稳定工具录制转MCP本文方案把人工操作固化成确定性工具固定流程、大批量重复、报表导出不适用于页面频繁改版和探索性操作在实践中我的判断是Browser Use适合让大模型去“探索”Playwright MCP适合让大模型去“临场指挥”录制转MCP则适合把成熟的操作流程“沉淀”下来。三者不是互相替代而是互补。面向业务落地录制路线胜在可控面向开发调试手段越灵活越好。5. 进阶玩法从“单个工具”到“工具库”5.1 录制一次处处调用这个工具的下一步演进思路是把录制结果当成可复用的资产。本来你为报表系统录制了一个查询导出工具之后另一个同事说“我还要把查询结果发送到企业微信群”只需要在原有录制的末尾追加一个发送步骤重新生成MCP server就多了一个更复合的工具。我建议按照“操作域”来组织录制内容一类流程对应一个项目一个项目里挂多个工具。比如“订单管理”域下有“订单查询”“订单导出”“订单备注修改”三个工具都挂在同一个MCP server上。这样客户端配置文件不用反复改大模型可在同一域内自行组合调用工具。5.2 录制时让大模型自动写工具描述可以尝试在录制工具里接入大模型能力录制完成后自动分析操作序列生成工具名和描述。好处是这样生成的描述更贴合操作语义而不是用户手动起名。比如录制完“点击导出按钮”系统自动生成名称export_report和描述“点击页面上的导出按钮下载当前查询结果为Excel报表”。这套提示词对后续大模型的工具理解准确率提升非常明显。在实际使用中大模型API调用产生的cost不高但对生成工具的描述质量提升是肉眼可见的。特别是当你积累了几十个工具后描述的好坏直接决定大模型能否在一堆工具里选中正确的那一个。5.3 实际使用中的边界与限制说完了优点也得说清楚这东西不适合哪些场景。页面结构变化频繁的站点每次改版你都得重新录制成本其实不低。涉及复杂拖拽、画布绘制、文件上传等非标准操作录制方案虽然能记录但回放稳定性大打折扣。那些需要验证码或強风控识别的流程录制工具能自动化操作但登录本身可能被网站识别为异常行为风险需要自己衡量。最稳妥的使用姿势是先小范围接入选1-2条最常用、最稳定的流程录制跑通后再扩展。这个工具适合“某某系统每天都要人工做一遍的操作”不适合做通用浏览器自动化平台。把定位想清楚使用的价值感会强很多。另外多说一句录制操作转MCP和时下流行的“Agent写网页自动化脚本”并不冲突。Agent的优势在于临场判断录制转MCP的优势在于确定性和可控性。两者结合才是操作大模型落地的最佳解法。我之前也看过不少关于Playwright MCP和Browser Use MCP的讨论各有侧重。但就“快速交付一个真实可用的MCP工具”这件事而言录制转MCP目前是我见过性价比最高的做法。你不需要理解AI如何决策不需要反复调整提示词只需要把人工操作录一遍。后续我还想在这个工具里加入录制后的步骤顺序调整、条件分支比如某个元素存在才执行某步、以及更智能的等待条件生成。这工具目前覆盖的场景还挺有限但每次用它把一个重复流程变成大模型能调用的工具时都会觉得“这事儿方向对了”。如果你也经常和大模型打交道、日常工作里又有些固定网页流程真的建议自己动手试一试这个思路说不定能帮你省下不少时间。