恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
构建Chrome扩展MCP服务器:为AI编程助手集成浏览器自动化能力
首页
资讯中心
/
构建Chrome扩展MCP服务器:为AI编程助手集成浏览器自动化能力
构建Chrome扩展MCP服务器:为AI编程助手集成浏览器自动化能力
发布时间:2026/9/2 5:52:31
在实际开发中我们常常需要将浏览器环境的能力如页面信息获取、DOM操作、网络请求拦截等集成到自动化脚本或AI辅助编程工具中。一个典型的场景是希望像“kimi-code”这类AI编程助手能够直接调用浏览器的功能例如自动填写表单、截图、提取数据或模拟用户交互从而扩展其代码生成和任务执行的能力。要实现这一点核心在于建立一个桥梁让外部工具能够安全、可控地与浏览器进行通信。本文的目标是构建一个Chrome扩展程序它不提供用户界面而是作为一个后台服务运行。这个扩展的核心功能是暴露一个基于MCPModel Context Protocol协议的服务器使得“kimi-code”或其他兼容MCP的客户端能够通过此协议将浏览器本身作为一个“工具表面”来调用。我们将从理解MCP协议与Chrome扩展的通信基础开始逐步完成扩展的Manifest V3配置、后台服务脚本编写、MCP服务器实现并最终实现一个从“kimi-code”客户端触发浏览器标签页截图的实际案例。整个过程将涵盖环境准备、代码实现、调试排错以及生产环境部署的注意事项。1. 理解核心概念MCP协议与Chrome扩展的通信机制在开始编码之前必须厘清几个关键概念这决定了我们整个项目的架构设计。1.1 什么是MCPModel Context ProtocolMCP是一种通信协议旨在为大型语言模型LLM或AI助手提供一个标准化的方式来发现、调用外部工具和访问上下文数据。你可以把它想象成AI世界的“API网关”或“RPC框架”。一个MCP服务器Server对外暴露一系列工具Tools和资源Resources而MCP客户端Client如kimi-code则可以连接到服务器列出可用的工具并调用它们。对于本项目而言我们的Chrome扩展将扮演MCP服务器的角色。它提供的“工具”就是各种浏览器操作例如capture_visible_tab捕获可见标签页、get_page_content获取页面内容等。kimi-code作为客户端通过MCP协议与我们的扩展通信从而间接操作浏览器。1.2 Chrome扩展作为MCP服务器的可行性Chrome扩展通常由几个部分组成manifest.json清单文件、背景脚本Background Script、内容脚本Content Script和弹出页面Popup。要让扩展成为一个常驻的服务器我们需要使用后台服务Service Worker这是Manifest V3中替代传统后台页面的技术。Service Worker在扩展安装后即可独立运行监听事件并保持活动状态非常适合作为常驻的MCP服务器。然而Service Worker运行在一个独立的、无DOM的环境中并且有生命周期限制可能在不活动时被终止。同时MCP协议通常基于标准输入输出stdio或WebSocket进行通信这与扩展的典型通信方式如chrome.runtimeAPI不同。因此我们需要在扩展中创建一个“适配层”可能通过chrome.runtime.onConnect或chrome.runtime.onMessage来模拟MCP服务器与外部Native Host一个本地应用的通信再由Native Host与kimi-code客户端进行stdio通信。这是本项目最大的架构挑战。1.3 技术栈与依赖关系基于以上分析我们确定以下技术栈和组件Chrome扩展 (Manifest V3): 提供浏览器API的访问权限运行后台Service Worker。MCP Server SDK (JavaScript/TypeScript): 用于实现MCP协议的服务端逻辑。我们可以使用官方或社区提供的JavaScript SDK。Native Host (可选但推荐): 一个本地应用程序作为扩展与kimi-code客户端之间的桥梁。它通过nativeMessaging与扩展通信并通过stdio与MCP客户端通信。这简化了扩展端的复杂度使其只需处理浏览器API和简单的消息转发。kimi-code 或 其他MCP客户端: 作为工具的调用方。本文将采用一种相对简洁的实现路径在Chrome扩展的Service Worker中直接实现一个简化版的MCP服务器并通过chrome.runtime.onConnectExternal监听来自外部连接。为了演示我们假设kimi-code客户端能够通过一个自定义的通信通道如WebSocket或特定的Chrome扩展消息端口连接到我们的扩展。在实际生产环境中可能需要配合Native Host。2. 环境准备与项目结构搭建在开始编写代码前需要准备好开发环境并创建清晰的项目目录。2.1 开发环境要求确保你的开发环境满足以下要求组件要求说明Node.js版本 16 或更高用于包管理和运行可能的构建脚本。npm 或 yarn最新稳定版包管理工具。Chrome / Chromium版本 88 或更高必须支持Manifest V3。代码编辑器VS Code 等推荐安装 Chrome 扩展开发相关插件。kimi-code 或 MCP 客户端支持 MCP 协议用于测试工具调用。本文将以一个模拟的客户端脚本进行演示。2.2 创建项目目录与初始化首先创建一个新的项目文件夹并初始化package.json。mkdir chrome-extension-mcp-server cd chrome-extension-mcp-server npm init -y接下来安装我们可能需要的依赖。由于我们将直接实现MCP协议逻辑这里选择安装一个基础的WebSocket库以便于扩展与外部测试客户端通信模拟MCP over WebSocket。同时我们也会安装TypeScript及相关类型定义以获得更好的开发体验。npm install ws types/ws --save-dev npm install typescript types/chrome --save-dev初始化TypeScript配置npx tsc --init编辑生成的tsconfig.json确保包含Chrome类型定义并输出到合适的目录例如dist。2.3 项目目录结构创建以下目录和文件形成清晰的项目结构chrome-extension-mcp-server/ ├── dist/ # TypeScript编译输出目录可选 ├── src/ # 源代码目录 │ ├── background/ # 后台Service Worker │ │ └── service-worker.ts │ └── utils/ # 工具函数 │ └── mcp-protocol.ts ├── public/ # 静态资源本例中可能为空 ├── manifest.json # 扩展清单文件 ├── package.json ├── tsconfig.json └── README.md3. 编写Chrome扩展核心Manifest V3配置manifest.json是扩展的“身份证”和“说明书”它定义了扩展的权限、资源和行为。对于我们的MCP服务器扩展关键配置如下{ manifest_version: 3, name: Browser MCP Server, version: 1.0.0, description: Exposes browser capabilities as tools via MCP protocol., permissions: [ activeTab, scripting, tabs, webNavigation, storage ], host_permissions: [ all_urls ], background: { service_worker: src/background/service-worker.js, type: module }, externally_connectable: { matches: [ https://kimi-code.example.com/* // 允许连接的特定来源生产环境需严格限制 ], ids: [*] // 允许所有扩展连接仅用于开发测试 }, content_security_policy: { extension_pages: script-src self; object-src self; } }关键配置解释manifest_version: 3必须使用Manifest V3。permissionsactiveTab允许在当前活动标签页执行操作如截图。scripting允许注入和执行脚本未来可能用于DOM操作。tabs允许查询和管理浏览器标签页。webNavigation监听页面导航事件可选用于上下文感知。storage用于存储配置或会话数据可选。host_permissions: [all_urls]允许扩展在所有网站上运行。这是高权限配置在生产扩展中应根据最小权限原则精确指定所需域名。background.service_worker指定后台Service Worker的入口文件。注意Manifest V3中Service Worker文件不能是TypeScript必须是JavaScript。因此我们需要将service-worker.ts编译为service-worker.js或者直接编写JS文件。本文示例将直接使用.js以简化。externally_connectable这是至关重要的配置。它定义了哪些外部网页或扩展可以连接到我们的扩展。为了让kimi-code或其本地代理能够连接我们必须在这里声明允许的来源。示例中使用了https://kimi-code.example.com作为占位符。在开发测试时可以暂时使用*但上线前必须修改为确切的、受信任的源否则会带来严重的安全风险。content_security_policy定义了扩展页面的安全策略防止XSS等攻击。我们采用了默认的严格策略。4. 实现后台Service Worker与MCP服务器逻辑这是扩展的核心。我们的Service Worker需要做两件事1) 实现MCP协议的消息处理2) 暴露浏览器工具。4.1 建立外部连接监听在src/background/service-worker.js中我们首先监听来自外部的连接。这里我们使用chrome.runtime.onConnectExternal它允许被externally_connectable中定义的源连接。// src/background/service-worker.js // 存储所有活动连接的端口 const connections new Map(); // 监听来自外部的连接例如从kimi-code的本地代理 chrome.runtime.onConnectExternal.addListener((port) { console.log([MCP Server] External connection established from: ${port.name}); // 为新连接分配一个唯一ID const connectionId conn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}; connections.set(connectionId, port); // 初始化MCP会话发送“initialized”通知根据MCP协议 port.postMessage({ jsonrpc: 2.0, method: notifications/initialized, params: {} }); // 监听来自客户端的消息 port.onMessage.addListener((message) { handleClientMessage(message, port, connectionId); }); // 处理连接断开 port.onDisconnect.addListener(() { console.log([MCP Server] Connection ${connectionId} disconnected.); connections.delete(connectionId); }); }); // 处理客户端消息的核心函数 async function handleClientMessage(message, port, connectionId) { console.log([MCP Server] Received message from ${connectionId}:, message); // 基本JSON-RPC 2.0校验 if (message.jsonrpc ! 2.0) { sendError(port, null, -32600, Invalid JSON-RPC version.); return; } const { id, method, params } message; try { switch (method) { case tools/list: await handleListTools(id, port); break; case tools/call: await handleCallTool(id, params, port); break; // 可以添加其他MCP方法如 resources/list, resources/read 等 default: sendError(port, id, -32601, Method not found: ${method}); } } catch (error) { console.error([MCP Server] Error handling method ${method}:, error); sendError(port, id, -32603, Internal error: ${error.message}); } } // 发送错误响应 function sendError(port, id, code, message) { port.postMessage({ jsonrpc: 2.0, id, error: { code, message } }); }4.2 实现MCP工具列表与调用接下来实现handleListTools和handleCallTool函数。这些函数定义了我们的扩展向客户端暴露了哪些浏览器工具。// src/background/service-worker.js (续) // 定义可用的工具列表 const availableTools [ { name: capture_visible_tab, description: Captures a screenshot of the currently active tab., inputSchema: { type: object, properties: { format: { type: string, enum: [png, jpeg], description: The image format., default: png }, quality: { type: integer, minimum: 0, maximum: 100, description: The quality of the capture (for jpeg)., default: 80 } } } }, { name: get_active_tab_info, description: Gets the URL and title of the currently active tab., inputSchema: { type: object, properties: {} // 此工具不需要参数 } } // 未来可以添加更多工具如 execute_script, navigate_to_url 等 ]; // 处理 tools/list 请求 async function handleListTools(requestId, port) { port.postMessage({ jsonrpc: 2.0, id: requestId, result: { tools: availableTools } }); } // 处理 tools/call 请求 async function handleCallTool(requestId, params, port) { const { name, arguments: toolArgs } params; let result; switch (name) { case capture_visible_tab: result await captureVisibleTab(toolArgs); break; case get_active_tab_info: result await getActiveTabInfo(); break; default: throw new Error(Tool not found: ${name}); } port.postMessage({ jsonrpc: 2.0, id: requestId, result: { content: [ { type: text, text: JSON.stringify(result, null, 2) } ] } }); }4.3 实现具体的浏览器工具函数现在实现具体的工具函数它们将调用Chrome扩展API。// src/background/service-worker.js (续) // 工具函数捕获当前可见标签页 async function captureVisibleTab(args {}) { const { format png, quality 80 } args; // 1. 获取当前活动标签页 const [activeTab] await chrome.tabs.query({ active: true, currentWindow: true }); if (!activeTab) { throw new Error(No active tab found.); } if (activeTab.url.startsWith(chrome://) || activeTab.url.startsWith(chrome-extension://)) { throw new Error(Cannot capture internal Chrome pages.); } // 2. 调用 captureVisibleTab API // 注意此API捕获的是调用者标签页的内容。在Service Worker中我们需要指定一个tabId。 // 这里我们使用 chrome.tabs.captureVisibleTab它捕获当前窗口的可见标签页。 const dataUrl await chrome.tabs.captureVisibleTab(null, { format, quality }); // 3. 返回结果。在实际MCP协议中可能需要返回资源引用或base64数据。 // 这里我们返回一个包含base64图像数据的对象。 return { success: true, tabId: activeTab.id, tabTitle: activeTab.title, tabUrl: activeTab.url, imageData: dataUrl, // data:image/png;base64,... format, timestamp: new Date().toISOString() }; } // 工具函数获取活动标签页信息 async function getActiveTabInfo() { const [activeTab] await chrome.tabs.query({ active: true, currentWindow: true }); if (!activeTab) { throw new Error(No active tab found.); } return { id: activeTab.id, title: activeTab.title, url: activeTab.url, status: activeTab.status, windowId: activeTab.windowId }; }5. 构建、加载扩展与运行验证代码编写完成后需要将其构建为扩展并加载到Chrome中进行测试。5.1 构建项目如果使用TypeScript如果你使用了TypeScript需要先编译。在package.json中添加脚本{ scripts: { build: tsc, watch: tsc --watch } }运行npm run build将TypeScript编译到dist目录。然后需要更新manifest.json中的service_worker路径指向编译后的JS文件例如dist/background/service-worker.js。5.2 加载未打包的扩展打开Chrome浏览器进入扩展管理页面 (chrome://extensions)。开启右上角的“开发者模式”。点击“加载已解压的扩展程序”。选择包含manifest.json的项目根目录chrome-extension-mcp-server。加载成功后你应该能在扩展列表中找到“Browser MCP Server”并看到其ID如aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa。5.3 创建测试客户端脚本为了验证扩展的MCP服务器是否工作我们需要一个模拟的MCP客户端。由于扩展通过chrome.runtime.connect接收连接我们可以写一个简单的Node.js脚本使用chrome-remote-interface或直接通过chrome.debugger协议来模拟连接。但更简单的方式是在浏览器内部创建一个测试页面利用externally_connectable配置进行连接。创建一个测试HTML文件test-client.html放在项目根目录下!DOCTYPE html html head titleMCP Client Test/title /head body h1MCP Client Test Page/h1 button idlistToolsList Tools/button button idcaptureTabCapture Tab/button button idgetTabInfoGet Tab Info/button pre idoutput/pre script const extensionId 你的扩展ID; // 替换为实际扩展ID let port null; function log(message) { document.getElementById(output).textContent message \n; } function connectToExtension() { if (port) return port; // 连接到扩展 port chrome.runtime.connect(extensionId, {name: test-client}); port.onMessage.addListener((msg) { log( Received: JSON.stringify(msg, null, 2)); }); port.onDisconnect.addListener(() { log(Disconnected from extension.); port null; }); log(Connected to extension.); return port; } function sendRequest(method, params) { const port connectToExtension(); const requestId Date.now(); const message { jsonrpc: 2.0, id: requestId, method, params }; log( Sending: JSON.stringify(message, null, 2)); port.postMessage(message); } document.getElementById(listTools).onclick () { sendRequest(tools/list, {}); }; document.getElementById(captureTab).onclick () { sendRequest(tools/call, { name: capture_visible_tab, arguments: { format: png } }); }; document.getElementById(getTabInfo).onclick () { sendRequest(tools/call, { name: get_active_tab_info, arguments: {} }); }; // 注意此页面必须通过 http/https 服务打开且域名需在 externall_connectable 的 matches 中。 // 为了方便你可以暂时修改 manifest.json 的 matches 为 [all_urls] 进行测试但完成后务必改回。 /script /body /html重要你需要通过一个本地HTTP服务器如python -m http.server 8000或npx serve来运行这个test-client.html页面并且其域名如http://localhost:8000必须添加到manifest.json的externally_connectable.matches中。同时将脚本中的extensionId替换为你扩展的实际ID。5.4 运行测试与验证修改manifest.json中的externally_connectable.matches临时加入http://localhost:8000/*。在Chrome中重新加载扩展在chrome://extensions页面点击扩展卡片上的刷新图标。启动本地HTTP服务器并在Chrome中打开http://localhost:8000/test-client.html。点击页面上的“List Tools”按钮。如果一切正常你应该在页面下方的输出区域看到来自扩展的响应其中包含capture_visible_tab和get_active_tab_info两个工具的定义。打开一个普通网页如https://www.example.com然后点击“Capture Tab”按钮。稍等片刻你应该会收到一个包含imageData一个很长的base64字符串的响应。你可以将这个base64字符串复制到浏览器的地址栏格式为data:image/png;base64,...来验证图片是否正确。点击“Get Tab Info”按钮应返回当前活动标签页的URL和标题。6. 常见问题排查与调试技巧在开发和测试过程中你可能会遇到以下问题。这里提供排查思路。6.1 连接失败或无法建立连接问题现象可能原因检查与解决点击按钮无任何响应控制台无错误。1. 扩展未正确加载或已禁用。2.externally_connectable配置不匹配。3. 测试页面的源协议、域名、端口未在matches中列出。1. 检查chrome://extensions确保扩展已启用且无错误。2. 仔细核对manifest.json中matches的每一个字符确保包含测试页面的完整源如http://localhost:8000。3. 在测试页面按F12打开开发者工具查看Console是否有“Cannot connect to extension”等错误。控制台报错Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.Service Worker可能已休眠或终止。Chrome为了节省资源会停止不活动的Service Worker。1. 确保在测试前有用户交互如点击按钮来“唤醒”Service Worker。2. 在Service Worker开头添加console.log观察其是否被重新启动。这是Manifest V3 Service Worker的正常行为你的代码需要能处理冷启动。6.2 MCP协议消息处理错误问题现象可能原因检查与解决收到响应但格式不符合JSON-RPC 2.0或返回了-32601 Method not found。1. 客户端发送的消息格式错误缺少jsonrpc: 2.0。2.method字段的值与服务器端switch语句中的case不匹配。1. 在handleClientMessage函数开始处打印收到的原始message检查其结构。2. 确保method字符串完全匹配包括大小写。MCP协议方法通常是tools/list和tools/call。调用capture_visible_tab失败返回权限错误或空白图片。1. 活动标签页是Chrome内部页面如chrome://extensions这些页面不允许截图。2. 标签页尚未完全加载完成。3. 扩展没有activeTab或all_urls权限。1. 在captureVisibleTab函数中添加对chrome://和chrome-extension://URL的检查并抛出友好错误。2. 确保目标网页是普通的HTTP/HTTPS页面并且已加载完毕。3. 确认manifest.json中的permissions和host_permissions已正确声明。6.3 Service Worker生命周期与状态管理问题现象可能原因检查与解决第一次调用成功但几分钟后调用失败需要刷新页面才能恢复。Service Worker因不活动被浏览器终止。所有变量状态如connectionsMap丢失。这是Manifest V3的预期行为。解决方案1.不要依赖Service Worker的内存状态。将需要持久化的数据如连接信息、会话使用chrome.storageAPI存储。2. 实现重连逻辑。客户端在发送消息前应检查端口状态如果断开则重新连接。3. 考虑使用chrome.alarmsAPI定期执行轻量任务以保持Service Worker活跃但需谨慎避免滥用。6.4 安全与权限警告问题现象风险与建议在扩展审核或用户安装时被提示权限过高特别是all_urls。host_permissions: [all_urls]是一个强大的权限可能会降低用户安装意愿或导致商店审核更严格。最佳实践1.按需申请如果工具只在用户主动点击时运行考虑使用activeTab权限它仅在用户与扩展交互后授予临时权限。2.可选权限对于某些高级功能可以使用chrome.permissions.request在运行时动态请求权限并清晰告知用户为何需要。3.限定域名如果只为特定网站服务将all_urls替换为具体的域名模式如[https://*.example.com/*]。7. 生产环境最佳实践与扩展方向一个可用于实际项目的MCP服务器扩展还需要考虑更多因素。7.1 安全加固严格限制连接源永远不要在生产环境的externally_connectable.matches中使用*。只允许受信任的、特定的源如kimi-code官方客户端的本地服务器地址http://127.0.0.1:某个特定端口。消息验证与鉴权在handleClientMessage中除了JSON-RPC格式校验还应验证消息来源。可以为每个连接设置一个简单的令牌Token鉴权机制。输入清理对从客户端接收的所有参数进行严格的类型和范围检查防止注入攻击。最小权限原则如前所述仔细审查permissions和host_permissions只申请必要的权限。7.2 健壮性提升错误处理与日志实现更精细的错误处理并将关键操作和错误记录到chrome.storage.local或远程日志服务便于排查。心跳与重连实现客户端与扩展之间的心跳机制及时发现连接断开并尝试重连。状态恢复将关键状态如注册的工具列表、活动连接信息持久化到chrome.storage以便Service Worker被终止后重启时能够恢复。工具调用超时为每个工具调用设置超时限制防止长时间运行的任务阻塞服务。7.3 功能扩展你现在已经拥有了一个基础的框架可以轻松添加更多强大的浏览器工具execute_script: 在指定标签页中执行JavaScript代码并返回结果。navigate_to_url: 控制浏览器导航到指定URL。extract_page_content: 获取页面的文本内容、链接或结构化数据。monitor_network_requests: 监听和拦截特定网络请求。manage_cookies: 读取或设置特定网站的Cookie。simulate_user_input: 模拟鼠标点击、键盘输入等用户交互。添加新工具只需三步在availableTools数组中定义新工具的名称、描述和输入模式。在handleCallTool函数的switch语句中添加新的case。实现对应的工具函数调用相应的Chrome API。7.4 与kimi-code等客户端的深度集成本文演示的是通过一个网页测试客户端进行连接。要与真正的kimi-code集成通常需要Native Host应用开发一个小的本地应用程序。该应用通过stdio与kimi-code作为MCP客户端通信同时通过nativeMessagingAPI与Chrome扩展通信。这样kimi-code就不需要直接处理浏览器扩展的连接细节。定义清晰的工具契约与kimi-code的开发者协作明确每个工具的名称、参数、返回值格式和语义确保双方理解一致。处理复杂的上下文浏览器操作往往依赖于当前页面状态。需要考虑如何将页面上下文如选中的元素、当前的登录状态安全地传递给AI模型并处理多标签页环境。通过以上步骤你构建的不仅仅是一个简单的Chrome扩展而是一个将浏览器强大能力开放给AI编程助手的标准化桥梁。这种模式可以极大地扩展AI助手在Web自动化、数据抓取、界面测试等场景的应用边界。在开始添加更复杂的功能之前请务必确保基础通信链路的安全与稳定。