
1. 当 Agent 需要“动手”而不是“动嘴”你可能已经习惯了让大模型写代码、总结文档、回答知识问题但真正让 Agent 产生业务价值的往往是它能像人一样打开网页、点击按钮、填写表单、把动态渲染后的数据抓回来。传统爬虫拿到的是空壳 HTMLAPI 又不一定开放而 Playwright / Selenium MCP 正好补上了这块能力把浏览器操作封装成 Agent 可调用的标准工具让模型自己决定“先点哪里、再填什么、最后抓哪段文本”。这篇文章面向三类人一是想把现有 Playwright/Selenium 脚本升级成 Agent 工具链的自动化工程师二是正在用 Cline、Claude Code、Cursor 等支持 MCP 的客户端希望让模型直接操作浏览器的开发者三是需要跑通“点击 填表 动态抓取”端到端流程、但不想在多个模型供应商之间反复切换 Key 的团队。我会给出 MCP server 配置片段、Agent 侧工具注册示例并用 TaoToken 统一 Key/API 通道跑通一次可复制的验证请求。实测下来把浏览器操作抽象成 MCP 工具后Agent 的自主决策链路会清晰很多排障也更容易定位。2. TaoToken 前置统一 Key 打通模型调用链在让 Agent 操作浏览器之前先要解决模型侧的调用问题。MCP 负责“工具层”但 Agent 的推理仍然依赖大模型。如果你同时用 Claude、GPT、国产模型做对比测试每个供应商一套 Key、一套 Base URL配置会非常散。TaoToken 的思路是提供一个统一的 API 通道把模型调用收敛到一处这样 MCP server 和 Agent 客户端只需要认一个 Key。你需要先拿到两样东西API Key 和 Base URL。API 地址是https://taotoken.net/api注意这个地址不加任何查询参数直接作为 OpenAI 兼容风格的 base_url 使用。Key 在控制台的 API Keys 页面创建建议按项目命名方便后续轮换。如果你用的是 Claude Code 这类工具它走的是 Anthropic 兼容通道同样可以用这个统一入口具体接入方式在文档里有对应说明。这里要强调一个容易踩的坑MCP server 本身不负责模型鉴权它只暴露浏览器工具模型鉴权发生在 Agent 客户端那一侧。所以配置要分两层看——Agent 客户端配置 TaoToken 的 Base URL Key Model IDMCP server 配置浏览器启动参数。两层都对了端到端才能跑通。我试过把这两层混在一起排查结果浪费了不少时间后来分开验证就快多了。对于长期跑编码和 Agent 任务的场景可以用 Coding Plan 来降低频繁调用带来的成本波动如果只是临时验证某个模型对工具调用的支持情况用模型对话页面直接测更轻量。接入文档里有完整的参数说明和示例建议先照着跑一遍最小请求确认 Key 有效再往下做 MCP。3. 可复制配置MCP server 与 Agent 工具注册这一节给出可直接复制的配置。先看 MCP server 的声明以 Playwright MCP 为例在支持 MCP 的客户端里通常用 JSON 描述 server 启动方式。下面这段是通用结构路径和命令按你本地实际安装调整{ mcpServers: { playwright: { command: npx, args: [ -y, playwright/mcplatest, --headless, --viewport-size1280,720 ], env: { PLAYWRIGHT_BROWSERS_PATH: 0 } } } }如果你更倾向 Selenium MCP配置结构类似只是 command 和 args 换成对应的 server 包。关键是command必须能被客户端找到args里的 headless 参数决定是否显示浏览器窗口调试阶段建议先关掉 headless方便肉眼确认点击位置。Agent 侧的工具注册本质是把 MCP server 暴露的工具列表拉过来转成模型能理解的 function schema。下面是一个简化的注册示例展示如何把browser_navigate、browser_click、browser_type、browser_get_text四个工具注册给 Agentimport json import httpx TAOTOKEN_BASE_URL https://taotoken.net/api TAOTOKEN_API_KEY 你的_API_Key MODEL_ID 你的_Model_ID # 从 MCP server 动态获取工具列表后转成 OpenAI 兼容的 tools 格式 tools [ { type: function, function: { name: browser_navigate, description: 导航到指定 URL, parameters: { type: object, properties: {url: {type: string}}, required: [url] } } }, { type: function, function: { name: browser_click, description: 点击页面元素, parameters: { type: object, properties: {selector: {type: string}}, required: [selector] } } }, { type: function, function: { name: browser_type, description: 在输入框填写文本, parameters: { type: object, properties: { selector: {type: string}, text: {type: string} }, required: [selector, text] } } }, { type: function, function: { name: browser_get_text, description: 获取元素文本, parameters: { type: object, properties: {selector: {type: string}}, required: [selector] } } } ] def call_agent(messages): resp httpx.post( f{TAOTOKEN_BASE_URL}/v1/chat/completions, headers{Authorization: fBearer {TAOTOKEN_API_KEY}}, json{ model: MODEL_ID, messages: messages, tools: tools, tool_choice: auto }, timeout60 ) return resp.json()注意MODEL_ID必须填你实际可用的模型标识不同模型对 tool calling 的支持程度不一样。如果模型返回的tool_calls为空先检查模型是否支持函数调用再检查 tools schema 是否合法。这套配置里 Base URL、Key、Model ID 三件套缺一不可任何一项写错都会在验证阶段暴露出来。4. 验证请求跑通一次点击加动态抓取配置完成后用一个最小场景验证打开一个带动态渲染的页面点击一个按钮填写一个输入框再抓取渲染后的文本。下面这段是 Agent 侧的完整调用循环包含工具执行和结果回填def execute_tool(name, args): # 这里对接真实的 MCP server示例用伪代码表示调用 if name browser_navigate: return mcp_client.call(browser_navigate, {url: args[url]}) if name browser_click: return mcp_client.call(browser_click, {selector: args[selector]}) if name browser_type: return mcp_client.call(browser_type, { selector: args[selector], text: args[text] }) if name browser_get_text: return mcp_client.call(browser_get_text, {selector: args[selector]}) raise ValueError(f未知工具: {name}) messages [ {role: user, content: 打开 https://example.com/search在搜索框输入 Playwright MCP点击搜索按钮然后告诉我结果数量} ] for _ in range(8): result call_agent(messages) choice result[choices][0][message] messages.append(choice) tool_calls choice.get(tool_calls) if not tool_calls: print(最终回答:, choice.get(content)) break for tc in tool_calls: fn tc[function][name] args json.loads(tc[function][arguments]) print(f调用工具: {fn} 参数: {args}) tool_result execute_tool(fn, args) messages.append({ role: tool, tool_call_id: tc[id], content: json.dumps(tool_result, ensure_asciiFalse) })成功的结果形态是Agent 先调用browser_navigate再调用browser_type填搜索词接着browser_click点按钮最后browser_get_text抓结果数量并把数字作为自然语言回答返回。如果中间某一步返回空或报错循环会把错误信息回填给模型模型有机会换选择器重试。这个“执行—回填—再推理”的闭环正是 MCP 相比写死脚本的优势所在。验证时建议先用一个结构简单的页面确认四个工具都能被正确触发再换成真实的动态站点。动态站点往往需要等待渲染可以在 MCP server 里配置默认超时或者在工具描述里提示模型“点击后可能需要等待”。5. 本篇常见错排查401、local proxy failed 与 reading choices排障时先看报错原文不要凭感觉改配置。下面几个是高频问题。第一个是401 Unauthorized。这几乎总是 Key 或 Base URL 的问题。检查Authorization头是不是Bearer加 Key中间有空格检查 Base URL 是不是https://taotoken.net/api不要多加/v1之外的路径也不要在 API 地址上附加查询参数。如果你把 Key 写进了 MCP server 的 env 而不是 Agent 客户端也会出现鉴权失败因为鉴权发生在模型调用那一层。第二个是local proxy failed或连接被拒绝。这类报错通常指向本地网络或 MCP server 进程没起来。先确认npx能正常拉取 server 包再确认客户端配置里的 command 路径正确。如果 server 启动后立刻退出把 headless 关掉看是否有浏览器内核缺失的提示必要时手动执行一次浏览器安装命令。第三个是reading choices或choices is undefined。这说明你拿到的响应不是标准的 chat completions 结构可能是请求打到了错误的端点或者模型返回了错误对象。打印完整响应体确认choices字段存在。如果响应里是error字段按错误信息处理常见的是模型 ID 不存在或该模型不支持 tools 参数。第四个是工具调用死循环。模型反复调用同一个工具却不给最终回答通常是工具返回内容太长或格式不清晰。把工具返回值精简成关键字段并在工具描述里写清楚返回结构能明显改善。如果用的是 Claude Code 走 Anthropic 兼容通道注意 OAuth 和 API Key 两种鉴权方式的配置位置不同混用会导致鉴权失败按文档选一种即可。6. 把浏览器能力接进你的 Agent 工作流到这里模型调用链和浏览器工具链已经打通。你可以把 Playwright MCP 用于需要精确等待和网络拦截的场景把 Selenium MCP 用于已有 Selenium 资产迁移的场景两者都通过统一的模型入口驱动。下一步建议是把这个最小闭环接到真实业务里比如竞品价格巡检、表单批量提交、动态报表抓取每接一个场景就补一组工具描述和超时配置。需要继续深入的话API Keys 和接入文档里有完整的鉴权与参数说明模型对话页面适合快速验证某个模型对 tool calling 的支持长期跑编码和 Agent 任务可以用 Coding Plan 控制调用成本。把 Key 管好、把工具描述写清楚、把错误回填做扎实Agent 操作浏览器的稳定性会比你预期的高。