1. 为什么要把 Playwright 塞进 MCP:Java 浏览器自动化服务器的真实场景
浏览器自动化这件事,写过爬虫或者做过端到端测试的朋友都不陌生。Playwright 作为微软开源的自动化框架,在 Java 生态里已经相当成熟,启动一个无头浏览器、点按钮、填表单、截图,几行代码就能跑起来。但问题在于,当你想让 AI 工具(比如 Claude、Cursor、Cherry Studio 这类支持 MCP 协议的客户端)去驱动浏览器时,直接暴露 Playwright 的 Java API 是不现实的——AI 客户端不认识你的 Java 方法签名,它只认 MCP 协议定义的工具调用格式。
MCP(Model Context Protocol)就是干这个的:它把「能力」抽象成一个个 tool,客户端通过 JSON-RPC 风格的请求来调用。你要做的,是用 Java 写一个 MCP 服务器,把 Playwright 的导航、截图、点击、填表这些动作包装成 MCP tool,然后让 AI 客户端连上来。这样 AI 就能说「帮我打开某个页面并截图」,你的 Java 服务收到请求后调 Playwright 执行,把结果返回去。
这个场景适合谁?我总结了三类:一是需要批量网页操作又要和 AI 联动的开发者,比如让 AI 自动填一批表单、抓一批页面状态;二是做自动化测试的团队,想把测试动作通过统一通道暴露给 AI 助手;三是自己在折腾 MCP 生态,想用 Java 而不是 Node/Python 来实现服务端的人。Java 的优势在于工程化成熟、依赖管理清晰、和现有 Spring 项目集成方便,尤其你如果本来就有 Spring Boot 服务,加一个 MCP 模块比另起一个 Node 进程省心得多。
但这里有个绕不开的环节:MCP 服务器本身要调用大模型能力(比如让模型决定下一步点哪里),或者你的 AI 客户端要通过一个统一的 API 通道来访问模型。如果每个工具、每个客户端都各自配一套 Key 和 endpoint,管理起来会很乱。所以这篇的核心思路是:Java 侧负责 Playwright 的浏览器能力,模型调用和统一通道走 TaoToken,把 endpoint 收敛到一处。下面我会从依赖、配置、代码、验证到排错,一步步把这条链路搭起来。
2. 前置准备:TaoToken 统一通道与 Java MCP 工程骨架
在写代码之前,先把两件事理清楚:一是模型通道怎么配,二是 Java 工程需要哪些依赖。
先说通道。TaoToken 提供的是统一的 API 入口,官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api 。它的作用是让你不用在多个模型供应商之间来回切换配置,一个 Key 就能走通。对于 MCP 服务器来说,这意味着你的 Java 服务在需要调用模型做决策时,Base URL 指向 TaoToken 的 API 地址即可,不用改一堆环境变量。
你需要先去控制台拿一个 API Key。地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,登录后在 API Keys 页面创建一个。创建完记得复制保存,页面上通常只显示一次。如果你还没决定用哪个模型,可以先去模型对话页面 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 看看有哪些可选,选一个适合工具调用的(一般选支持 function calling 的模型)。
再说 Java 工程。我用的是 Maven 项目,核心依赖两块:Playwright 和 MCP 官方 Java SDK。Playwright 版本我用的 1.41.2,MCP SDK 用的 0.10.0。如果你要做 HTTP SSE 传输(让远程客户端能连),还需要加 Spring WebFlux 或 WebMVC 的传输依赖。下面是完整的 pom 片段:
<dependencies> <!-- Playwright 浏览器自动化 --> <dependency> <groupId>com.microsoft.playwright</groupId> <artifactId>playwright</artifactId> <version>1.41.2</version> </dependency> <!-- MCP 官方 Java SDK --> <dependency> <groupId>io.modelcontextprotocol.sdk</groupId> <artifactId>mcp</artifactId> <version>0.10.0</version> </dependency> <!-- HTTP SSE 传输:基于 Spring WebFlux --> <dependency> <groupId>io.modelcontextprotocol.sdk</groupId> <artifactId>mcp-spring-webflux</artifactId> <version>0.10.0</version> </dependency> <!-- Spring Boot 基础(如果你用 Spring 管理生命周期) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies>这里有个坑我踩过:MCP SDK 的版本要和传输模块版本对齐,0.10.0 的 mcp 配 0.10.0 的 mcp-spring-webflux,混用版本会出现类找不到或者方法签名不匹配。另外 Playwright 的驱动不是 Maven 依赖自动带的,需要单独安装,这个后面第 4 节会讲。
工程结构上,我建议至少分三层:一个 PlaywrightManager 负责浏览器实例的生命周期(初始化、关闭),一个 ToolRegistry 负责注册各个 MCP tool,一个 ServerBootstrap 负责启动 MCP 服务器。这样职责清晰,后面加工具不会乱。如果你只是快速验证,也可以全塞一个类里,但工具一多就会很难维护。
关于模型通道的配置,我建议单独放一个配置文件,比如 application.yml 或者一个 properties 文件,把 Base URL 和 Key 抽出来:
taotoken: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} model: your-preferred-model-id用环境变量注入 Key 是基本的安全习惯,别把 Key 硬编码进代码提交到仓库。后面第 3 节我会给出更完整的配置片段,包括 MCP 客户端侧的 settings 写法。
3. 可复制配置:Java MCP 服务端、Playwright 启动参数与客户端 settings
这一节是整篇的核心,我会把三份配置都给全:Java 服务端的 MCP 初始化、Playwright 的启动参数、以及客户端(以支持 MCP 的编辑器为例)的 settings 片段。你照着改路径和 Key 就能跑。
先看 Java 服务端的 MCP 初始化。核心是用 McpServer.sync(transportProvider) 构建同步服务器,声明 serverInfo 和 capabilities,然后把各个 tool 注册进去。transportProvider 根据你选的传输方式不同而不同,如果用 WebFlux SSE,大概是这样的:
@Configuration public class McpServerConfig { private final McpTransportProvider transportProvider; public McpServerConfig(McpTransportProvider transportProvider) { this.transportProvider = transportProvider; } @PostConstruct public void start() { McpSyncServer syncServer = McpServer.sync(transportProvider) .serverInfo("Playwright-Mcp-Server", "1.0.0") .capabilities(McpSchema.ServerCapabilities.builder() .tools(true) .logging() .build()) .build(); try { syncServer.addTool(navigate()); syncServer.addTool(screenshot()); syncServer.addTool(click()); syncServer.addTool(fill()); syncServer.addTool(select()); syncServer.addTool(hover()); syncServer.addTool(evaluate()); syncServer.addTool(closePage()); syncServer.loggingNotification(McpSchema.LoggingMessageNotification.builder() .level(McpSchema.LoggingLevel.DEBUG) .logger("playwright-mcp") .data("Server initialized") .build()); } catch (Exception e) { log.error("注册工具失败: {}", e.getMessage(), e); } } }Playwright 的启动参数是另一个关键点。默认情况下 Playwright 会下载自己的 Chromium,但如果你机器上已经有 Edge 或者想用系统浏览器,可以指定 channel。下面这段是初始化逻辑,注意 headless 参数——调试阶段建议设 false,能看到浏览器窗口,方便确认操作对不对;上线再改 true。
private Playwright playwright; private Browser browser; private Page page; private void initializePlaywright() { if (playwright == null) { playwright = Playwright.create(); } if (browser == null) { browser = playwright.chromium().launch( new BrowserType.LaunchOptions() .setChannel("msedge") // 用系统 Edge,避免重复下载 .setHeadless(false) // 调试期开窗口 .setSlowMo(100) // 每步慢 100ms,方便观察 ); } if (page == null) { page = browser.newPage(); } }如果你不想用 Edge,把 setChannel 去掉就会用 Playwright 自带的 Chromium。setSlowMo 这个参数在调试时特别有用,能让每个动作之间有间隔,肉眼能跟上。
接下来是客户端侧的 settings。以支持 MCP 的编辑器为例,通常是在配置文件里声明一个 mcpServers 节点,指定命令、参数和环境变量。如果你用的是 SSE 传输,配置的是 URL;如果是 stdio 传输,配置的是启动命令。下面给一个 SSE 方式的 JSON 片段:
{ "mcpServers": { "playwright-java": { "url": "http://localhost:8080/sse", "env": { "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_API_KEY": "你的Key", "TAOTOKEN_MODEL": "你的模型ID" } } } }如果你用的是 Cline 或者 Claude Code 这类工具,配置文件的路径和字段名会略有不同,但核心三件套是一样的:Base URL、Key、Model ID。Base URL 填 https://taotoken.net/api ,Key 填你在控制台创建的那个,Model ID 填你选的模型标识。这三样对齐了,客户端才能正确路由请求。
这里要提醒一句:MCP 服务器本身不一定要调模型,它只是暴露工具。真正调模型的是客户端。所以 TaoToken 的配置主要影响的是客户端侧,以及你的 Java 服务如果内部有模型调用逻辑的话。把 endpoint 统一到 TaoToken,好处是你换模型、换供应商时只改一处,不用动 Java 代码。
4. 端到端验证:从驱动安装到一次 navigate + screenshot 成功返回
配置写完了,接下来是验证。这一步我建议按顺序来:先装驱动,再启动服务,最后用客户端发一次真实请求。
第一步,安装 Playwright 驱动。虽然程序会自动检测并安装,但自动安装依赖网络,经常失败。手动装更稳:
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install"这条命令默认装 webkit、chromium、firefox 三个。如果你只用 Chromium,可以指定:
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install chromium"装完之后,驱动会放在用户目录下的缓存里,比如~/.cache/ms-playwright(Linux/Mac)或者%USERPROFILE%\AppData\Local\ms-playwright(Windows)。你可以去这个目录确认一下有没有对应的文件夹。
第二步,启动你的 Java MCP 服务。如果是 Spring Boot 项目,直接 run 主类;如果是普通 Java 项目,用 mvn exec 启动。启动后看日志,应该能看到「Server initialized」这条 DEBUG 日志,说明 MCP 服务器起来了,工具也注册成功了。如果日志里报「创建 JSON Schema 时发生错误」,多半是某个 tool 的 schema 字符串格式有问题,检查一下 JSON 是否合法。
第三步,在客户端里发一次请求。我用的是 navigate 加 screenshot 的组合,先打开一个页面,再截图确认。在客户端的对话里输入类似这样的指令:
请调用 navigate 工具打开 https://example.com ,然后调用 screenshot 工具截图。
客户端会把这两个请求转成 MCP tool call 发到你的 Java 服务。你的服务收到后,navigate 会调 page.navigate(url),screenshot 会调 page.screenshot() 并把图片数据返回。如果一切正常,客户端会显示「Navigated to https://example.com」和一张截图。
这里有个细节:screenshot 返回的是二进制数据,MCP 协议里通常用 base64 编码的 ImageContent 来传。你的 tool 实现里要把它包装成 McpSchema.ImageContent,而不是 TextContent。如果客户端显示的是乱码或者报「reading choices」之类的错,多半是内容类型没对上。
验证成功的标志有三个:一是客户端收到了 navigate 的成功返回;二是截图能正常显示;三是你的 Java 服务日志里没有异常堆栈。三个都满足,说明整条链路通了。这时候你可以再试一个复杂点的动作,比如 fill 填表单加 click 点按钮,确认交互类工具也正常。
如果验证失败,别急着改代码,先看第 5 节的排错对照表,大部分问题都能在那里找到答案。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth 逐条对照
这一节我把实际遇到过的几类报错整理出来,每条给出原因和解决方向。你对照自己的日志看。
401 Unauthorized。这个最常见,基本是 Key 的问题。三种可能:Key 没填、Key 填错、Key 过期。先检查客户端 settings 里的 TAOTOKEN_API_KEY 是不是完整复制了,有没有多余空格。如果 Key 是对的,检查 Base URL 是不是 https://taotoken.net/api ,路径写错也会导致鉴权失败。还有一种情况是你用了环境变量但没生效,可以在 Java 里打印一下 System.getenv 确认。
local proxy failed。这个报错通常出现在客户端尝试连接 MCP 服务器时。原因可能是你的 Java 服务没启动、端口不对、或者防火墙拦了。先确认服务在监听(netstat 或 lsof 看端口),再确认客户端配置的 URL 和实际端口一致。如果是 SSE 传输,URL 一般是 http://localhost:8080/sse,别漏了 /sse 后缀。
reading choices 相关错误。这个多半是模型返回格式和客户端预期不一致。如果你在 Java 服务里调了模型,检查一下请求体里的 model 字段是不是填了正确的 Model ID。有些客户端对返回的 JSON 结构有要求,如果模型返回的是流式但客户端按非流式解析,也会报这个。解决方法是确认 Model ID 和客户端支持的调用方式匹配。
OAuth 相关报错。如果你用的是 Claude Code 这类需要 OAuth 的工具,报 OAuth 错误通常是认证流程没走完或者 token 过期。这种情况建议检查你的认证配置,确认 token 有效。如果是在 MCP 服务器侧,一般不会涉及 OAuth,除非你的服务本身做了鉴权。
除了这四类,还有几个小坑:Playwright 驱动没装会报「Executable doesn't exist」,回去跑第 4 节的安装命令;端口被占用会报「Address already in use」,换个端口或者杀掉占用进程;MCP SDK 版本不匹配会报「NoSuchMethodError」,检查 pom 里 mcp 和 mcp-spring-webflux 版本是否一致。
排查的时候有个通用技巧:先把日志级别调到 DEBUG,看完整的请求和响应。MCP 的交互是 JSON-RPC,请求和响应都能在日志里看到,对照着看哪一步断了,比猜要快得多。
6. 把通道收敛到 TaoToken:长期编码与 Agent 场景的接入建议
链路跑通之后,最后聊聊怎么把它用得更顺。核心思路是把模型通道收敛到 TaoToken,让你的 Java MCP 服务和客户端都指向同一个 endpoint。
对于长期做编码或者 Agent 场景的朋友,我建议关注 Coding Plan。地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。它的定位是给需要持续调用模型的开发场景用的,比按次调用更适合高频使用。如果你的 MCP 服务每天要处理大量浏览器自动化任务,每次都要调模型做决策,用 Coding Plan 会比零散调用省心。
接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的 API 说明和示例。API Keys 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,你可以在这里创建多个 Key,按项目或者环境分开,方便管理和轮换。
如果你用的是 Claude Code 这类工具,它有自己的接入方式,可以参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=ClaudeCodeAnthropic&utm_campaign=rewrite 里的说明。核心还是那三件套:Base URL、Key、Model ID,配对了就能用。
最后给一个实用建议:把 Playwright 的浏览器实例做成可复用的,别每次 tool 调用都重新 launch。我的做法是在 PlaywrightManager 里维护单例,第一次调用时初始化,后续复用,服务关闭时统一释放。这样批量操作时性能会好很多。另外,截图和页面内容这类大返回,注意控制大小,必要时压缩或者只返回关键区域,避免把客户端撑爆。
这套东西我实测下来,从零搭到跑通大概半天,主要时间花在驱动安装和版本对齐上。你把第 3 节的配置抄过去,改改路径和 Key,应该能更快。