Skip to main content
Glama

Chrome Debug MCP Server

by Rainmen-xia
index.ts9.14 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, CallToolResult, } from "@modelcontextprotocol/sdk/types.js"; import { BrowserSession } from "./browserSession.js"; /** * MCP浏览器自动化服务器 * 提供Chrome调试端口连接和浏览器操作功能 */ class BrowserMCPServer { private server: Server; private browserSession: BrowserSession; constructor() { this.server = new Server( { name: "browser-automation-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); this.browserSession = new BrowserSession(); this.setupToolHandlers(); this.setupErrorHandling(); } /** * 设置工具处理器 */ private setupToolHandlers(): void { // 注册工具列表处理器 this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "launch_browser", description: "启动浏览器连接,连接到Chrome调试端口以保持登录状态", inputSchema: { type: "object", properties: { remote_host: { type: "string", description: "可选的远程Chrome主机URL (例如: http://localhost:9222)", }, }, }, }, { name: "close_browser", description: "关闭浏览器连接", inputSchema: { type: "object", properties: {}, }, }, { name: "navigate_to", description: "导航到指定URL,智能管理标签页(相同域名复用标签页)", inputSchema: { type: "object", properties: { url: { type: "string", description: "要导航到的URL", }, }, required: ["url"], }, }, { name: "click", description: "在指定坐标位置点击", inputSchema: { type: "object", properties: { coordinate: { type: "string", description: "点击位置的坐标,格式为 'x,y'", }, }, required: ["coordinate"], }, }, { name: "type_text", description: "输入文本内容", inputSchema: { type: "object", properties: { text: { type: "string", description: "要输入的文本内容", }, }, required: ["text"], }, }, { name: "scroll_down", description: "向下滚动页面一个视口高度", inputSchema: { type: "object", properties: {}, }, }, { name: "scroll_up", description: "向上滚动页面一个视口高度", inputSchema: { type: "object", properties: {}, }, }, { name: "hover", description: "将鼠标悬停在指定坐标位置", inputSchema: { type: "object", properties: { coordinate: { type: "string", description: "悬停位置的坐标,格式为 'x,y'", }, }, required: ["coordinate"], }, }, { name: "resize_browser", description: "调整浏览器窗口大小", inputSchema: { type: "object", properties: { size: { type: "string", description: "窗口大小,格式为 'width,height'", }, }, required: ["size"], }, }, { name: "get_page_content", description: "获取当前页面的HTML内容", inputSchema: { type: "object", properties: {}, }, }, ] as Tool[], }; }); // 注册工具调用处理器 this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { let result; switch (name) { case "launch_browser": result = await this.browserSession.launchBrowser(args?.remote_host as string); break; case "close_browser": result = await this.browserSession.closeBrowser(); break; case "navigate_to": if (!args?.url) { throw new Error("URL参数是必需的"); } result = await this.browserSession.navigateToUrl(args.url as string); break; case "click": if (!args?.coordinate) { throw new Error("coordinate参数是必需的"); } result = await this.browserSession.click(args.coordinate as string); break; case "type_text": if (!args?.text) { throw new Error("text参数是必需的"); } result = await this.browserSession.type(args.text as string); break; case "scroll_down": result = await this.browserSession.scrollDown(); break; case "scroll_up": result = await this.browserSession.scrollUp(); break; case "hover": if (!args?.coordinate) { throw new Error("coordinate参数是必需的"); } result = await this.browserSession.hover(args.coordinate as string); break; case "resize_browser": if (!args?.size) { throw new Error("size参数是必需的"); } result = await this.browserSession.resize(args.size as string); break; case "get_page_content": result = await this.browserSession.getPageContent(); break; default: throw new Error(`未知工具: ${name}`); } // 构建响应内容 const content = []; if (result.error) { content.push({ type: "text" as const, text: `错误: ${result.error}`, }); } else { // 添加成功消息 content.push({ type: "text" as const, text: this.getSuccessMessage(name, result), }); // 如果有截图,添加截图 if (result.screenshot) { // 从data URL中提取纯Base64数据和MIME类型 const dataUrlMatch = result.screenshot.match(/^data:image\/([^;]+);base64,(.+)$/); if (dataUrlMatch) { const [, imageType, base64Data] = dataUrlMatch; content.push({ type: "image" as const, data: base64Data, // 只传递纯Base64数据,不包含data URL前缀 mimeType: `image/${imageType}`, }); } else { // 如果不是标准的data URL格式,直接使用原始数据 content.push({ type: "image" as const, data: result.screenshot, mimeType: "image/png", // 默认类型 }); } } // 如果有日志,添加日志 if (result.logs && result.logs.trim()) { content.push({ type: "text" as const, text: `日志信息:\n${result.logs}`, }); } } return { content, isError: !!result.error, } as CallToolResult; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text" as const, text: `执行工具 "${name}" 时发生错误: ${errorMessage}`, }, ], isError: true, } as CallToolResult; } }); } /** * 根据工具名称和结果生成成功消息 */ private getSuccessMessage(toolName: string, result: any): string { switch (toolName) { case "launch_browser": return "✅ 浏览器连接成功建立"; case "close_browser": return "✅ 浏览器连接已关闭"; case "navigate_to": return `✅ 成功导航到: ${result.currentUrl || "目标页面"}`; case "click": return `✅ 点击操作完成${result.currentMousePosition ? ` (位置: ${result.currentMousePosition})` : ""}`; case "type_text": return "✅ 文本输入完成"; case "scroll_down": return "✅ 向下滚动完成"; case "scroll_up": return "✅ 向上滚动完成"; case "hover": return `✅ 悬停操作完成${result.currentMousePosition ? ` (位置: ${result.currentMousePosition})` : ""}`; case "resize_browser": return "✅ 浏览器窗口大小调整完成"; case "get_page_content": return "✅ 页面内容获取完成"; default: return "✅ 操作完成"; } } /** * 设置错误处理 */ private setupErrorHandling(): void { this.server.onerror = (error) => { console.error("[MCP服务器错误]", error); }; process.on("SIGINT", async () => { console.log("\n正在关闭MCP服务器..."); await this.browserSession.closeBrowser(); process.exit(0); }); process.on("SIGTERM", async () => { console.log("\n正在关闭MCP服务器..."); await this.browserSession.closeBrowser(); process.exit(0); }); } /** * 启动服务器 */ async run(): Promise<void> { const transport = new StdioServerTransport(); console.error("MCP浏览器自动化服务器启动中..."); await this.server.connect(transport); console.error("✅ MCP服务器已启动,等待连接..."); } } // 启动服务器 const server = new BrowserMCPServer(); server.run().catch((error) => { console.error("启动服务器失败:", error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Rainmen-xia/chrome-debug-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server