浏览器扩展这个赛道,这两年肉眼可见地卷了起来。以前大家装扩展,无非是广告拦截、密码管理、截图标注这几样,工具属性极强,用完即走。但最近半年,一个明显的变化是:越来越多的扩展开始往"常驻助手"的方向走——不是等你点开才工作,而是安静地待在每一个标签页里,在你需要的时候恰好出现。EO2Weave 上架 Chrome 应用商店这件事,在我看来就是这条路线上的一个典型样本。它主打的是把 AI 助手能力织进浏览器的日常动线里,关键词里出现的 Codex OAuth 和 WebMCP 两个词,基本暴露了它的技术底牌:一个负责身份与授权打通,一个负责让网页本身变成可被调用的能力接口。这篇内容我打算从扩展的定位、授权链路、WebMCP 的落地方式、上架前后的实操细节几个角度,把这类"住进标签页"的扩展拆开讲清楚,适合正在做浏览器扩展的开发者、想理解 AI 助手如何嵌入浏览器工作流的产品同学,以及单纯好奇这类工具怎么跑起来的普通用户。
1. 为什么"住进标签页"是个值得认真对待的产品判断
1.1 从"工具调用"到"环境感知"的转变
传统扩展的交互模型是"用户发起—扩展响应"。你点图标,弹窗出来,操作完关掉。这个模型的问题在于,它把扩展放在了一个被动的位置,用户必须记得它的存在,必须主动去调用。而"住进标签页"的思路完全不同:扩展在后台持续感知当前页面的状态,理解你正在做什么,然后在合适的时机把能力递上来。
这个转变背后有一个很实际的观察——人在浏览器里的工作流是高度碎片化的。你可能在查文档、填表单、对比价格、读论文、写邮件之间反复横跳,每一次切换都伴随着上下文的丢失。如果 AI 助手只能在某个独立窗口里等你提问,那它和搜索引擎的区别其实不大。但如果它能跟着你的标签页走,知道你当前在看什么、选了什么、填到哪一步了,那它提供的就不是"问答",而是"接力"。
EO2Weave 这个名字里的 Weave(编织)其实挺传神。它不是要做一个大而全的入口,而是把能力像线一样织进你已有的浏览行为里。这个定位决定了它的技术架构必须是轻量、常驻、低打扰的,也决定了它必须解决一个核心问题:怎么在不侵犯隐私的前提下,让扩展"看懂"当前页面。
1.2 常驻型扩展的三个硬门槛
说起来容易,做起来难。一个想常驻在每个标签页里的扩展,至少要跨过三道坎。
第一道是性能坎。Chrome 对扩展的资源占用是有隐性容忍度的,如果你的 content script 在每个页面都跑重逻辑,用户很快就会感觉到卡顿,然后毫不犹豫地禁用你。所以常驻型扩展的核心计算必须尽量放在 background service worker 里,content script 只做最轻量的感知和注入。
第二道是权限坎。你想"看懂"页面,就得申请相应的 host permissions,但权限申请得越宽,用户安装时的犹豫就越重。Chrome 应用商店对权限的描述审核也越来越严,笼统地写"读取和更改您在所有网站上的数据"基本等于劝退。合理的做法是按需申请,或者用 activeTab 这类临时权限配合用户手势触发。
第三道是上下文坎。标签页之间是隔离的,扩展要跨标签页维持一个连贯的助手状态,就得自己设计状态同步机制。这里涉及 storage、message passing、以及 service worker 生命周期管理的一堆细节,稍不注意就会出现"助手失忆"的情况。
EO2Weave 能上架,说明这三道坎它至少给出了可用的答案。下面我会结合 Codex OAuth 和 WebMCP 这两个关键词,把它的实现思路拆开讲。
2. Codex OAuth 在扩展里到底解决了什么问题
2.1 扩展做身份授权的天然困境
浏览器扩展做用户身份,一直是个麻烦事。你没有自己的域名回调页,没有传统的服务端 session 可以依赖,弹出式授权窗口又容易被拦截。更关键的是,AI 类扩展通常需要调用后端模型服务,这就意味着必须有一个可靠的身份凭证来计费、限流、区分用户。
常见的做法有两种:一种是自己搭一套账号体系,用户注册登录,扩展存 token;另一种是接第三方 OAuth。前者开发成本高,用户还要多记一套密码;后者体验好,但回调地址的处理在扩展环境里很别扭。
Codex OAuth 这个关键词指向的,应该是一套基于 OAuth 流程的授权方案,专门为扩展这类无固定回调域的场景做了适配。它的核心思路通常是:扩展发起授权请求,跳转到授权页,用户确认后,授权方通过一个约定的机制把凭证回传给扩展,而不是依赖传统的 redirect_uri。
2.2 授权链路的关键节点与踩坑点
如果你正在做类似的集成,这条链路上有几个节点特别容易出问题。
第一个节点是授权窗口的打开方式。在扩展里,你不能直接用 window.open 去开授权页,因为很多授权方会检测窗口来源。更稳的做法是用 chrome.identity.launchWebAuthFlow,它专门为扩展设计,能拿到最终的跳转 URL,从中解析出授权码或 token。这个 API 在 MV3 里的行为和 MV2 有差异,需要确认你的 manifest 版本和权限配置。
第二个节点是 token 的存储。拿到 token 之后,存哪里是个学问。chrome.storage.local 是明文存储,chrome.storage.session 在 MV3 里是内存级、浏览器关闭即清空。对于长期凭证,通常需要配合服务端的 refresh token 机制,扩展本地只存短期 access token。这里有个实操经验:不要把 refresh token 放在扩展里,一旦扩展被逆向,等于把长期钥匙交出去了。
第三个节点是 token 过期后的静默刷新。用户不会容忍每次打开扩展都重新授权。你需要在 background service worker 里维护一个刷新逻辑,在 token 临近过期时自动用 refresh token 换新的。但 MV3 的 service worker 会被浏览器随时挂起,所以刷新逻辑不能依赖内存状态,必须每次从 storage 读取当前 token 状态再判断。
提示:MV3 的 service worker 生命周期是这类扩展最大的坑之一。任何依赖"常驻内存变量"的设计都会在 service worker 被回收后失效,所有状态必须持久化到 storage。
2.3 为什么选 OAuth 而不是自建账号
从产品角度看,选 Codex OAuth 这类方案,本质是在用授权方的账号体系换自己的开发成本和用户信任成本。用户不需要为新扩展再注册一次,授权页上能看到明确的权限范围,心理门槛低很多。对开发者来说,计费和限流可以挂在授权方的体系上,省掉一整套账号后端。
代价是依赖。授权方的接口变更、政策调整、服务可用性,都会直接影响你的扩展。所以合理的架构是:OAuth 只负责身份和凭证,核心业务逻辑尽量与授权方解耦,这样即使将来换授权方案,迁移成本也可控。
3. WebMCP:让网页变成可被调用的能力接口
3.1 WebMCP 想解决的核心矛盾
WebMCP 这个词拆开看,MCP 通常指 Model Context Protocol 这类让模型与外部能力对接的协议思路,加上 Web 前缀,指向的应该是"让网页本身成为模型可调用的上下文来源"。
这里有一个长期存在的矛盾:AI 助手想帮你操作网页,但它"看不见"网页的结构化语义。它拿到的是 DOM 树、是 HTML 字符串,里面混杂着样式、脚本、广告、无关内容。让模型直接读原始 DOM,既慢又不准,还容易触发注入风险。
WebMCP 的思路是,在网页和模型之间加一层语义抽象。网页通过某种约定,把自己的关键能力(比如"这个页面有一个搜索框""这个列表可以被筛选""这个按钮会提交表单")暴露成结构化的描述,模型通过这层描述来理解和操作页面,而不是硬啃 DOM。
3.2 扩展如何充当这层抽象的载体
浏览器扩展恰好是承载这层抽象的天然位置。它既能注入 content script 去读取和标注页面,又能在 background 里和模型服务通信,还能通过 message passing 在两者之间传递结构化数据。
具体到实现,通常有这么几步。扩展的 content script 在页面加载后,扫描页面中的关键交互元素,按照 WebMCP 约定的格式生成一份"能力清单"。这份清单不是原始 DOM,而是提炼后的语义描述,比如每个可交互元素的角色、标签、当前状态、可执行的动作。然后这份清单被送到 background,再由 background 转发给模型侧。模型决定要执行某个动作时,指令沿着反方向传回 content script,由它去实际触发页面上的元素。
这个链路听起来简单,但实操中有几个细节决定成败。一是元素定位的稳定性,你不能依赖易变的 CSS 类名,最好用语义角色加文本内容组合定位。二是动作执行的时序,很多页面是异步渲染的,元素出现有延迟,需要配合 MutationObserver 做等待。三是权限边界,哪些页面允许被操作、哪些动作需要用户二次确认,必须在扩展层面设好闸门。
3.3 一个容易被忽略的安全考量
让 AI 操作网页,风险是实打实的。如果扩展能自动点击、自动填表、自动提交,那它被滥用或出错的后果可能很严重。所以 WebMCP 这类方案在落地时,通常会设计几层防护。
一层是动作分级。读取类动作(比如提取页面文本)可以自动执行,写入类动作(比如填表单)需要用户确认,提交类动作(比如点击支付)必须显式授权。另一层是域名白名单,扩展只在用户明确信任的站点上启用操作能力。还有一层是操作日志,每一步动作都记录下来,出问题时可追溯。
注意:任何让模型直接操作页面的设计,都必须假设模型会犯错。防护机制不是可选项,是必选项。
4. 从开发到上架:Chrome 应用商店的实操细节
4.1 上架前的自查清单
Chrome 应用商店的审核这几年越来越细,尤其是涉及 AI 能力和页面操作的扩展,审核员会重点看你的权限申请是否合理、隐私政策是否清晰、数据处理是否透明。上架前建议对照下面这张表逐项过一遍。
| 检查项 | 常见问题 | 建议做法 |
|---|---|---|
| 权限申请 | 申请了过宽的 host permissions | 用 activeTab 或按需申请,manifest 里写清用途 |
| 隐私政策 | 缺失或过于笼统 | 明确说明收集哪些数据、如何使用、是否上传 |
| 远程代码 | 从远端加载并执行脚本 | MV3 禁止远程代码,所有逻辑必须打包在扩展内 |
| 单一用途 | 功能过于庞杂 | 扩展描述聚焦一个核心用途,避免"万能工具"定位 |
| 截图与描述 | 与实际功能不符 | 截图展示真实界面,描述不夸大 |
4.2 MV3 迁移中的典型报错与处理
如果你是从 MV2 迁过来的,大概率会遇到几个经典报错。background page 变成 service worker 后,原本用 window、document 的代码会直接报错,因为 service worker 里没有这些全局对象。解决办法是把涉及 DOM 的逻辑全部挪到 content script 或 offscreen document 里。
另一个高频问题是持久化连接。MV2 里可以用长连接 port 维持 background 和 content script 的通信,MV3 里 service worker 被挂起后连接会断。稳妥的做法是改用一次性消息加事件驱动,每次通信都重新建立,不要假设连接一直活着。
还有一个是定时任务。setInterval 在 service worker 里不可靠,因为 worker 随时可能被回收。需要定时执行的逻辑,应该用 chrome.alarms API,它由浏览器统一调度,不受 worker 生命周期影响。
4.3 审核被拒后的应对思路
被拒不可怕,可怕的是不知道为什么被拒。Chrome 的拒审通知通常会给出一个笼统的理由,比如"权限使用不当"或"功能描述不清"。这时候不要盲目改一版重新提交,而是先定位问题。
我的经验是,先检查权限列表,把每一个权限和实际使用场景对应起来,用不上的坚决删掉。然后检查隐私政策,确保它明确提到了扩展会接触哪些数据。最后检查功能描述,确保它和实际行为一致,没有夸大或模糊。改完之后,在提交说明里简要解释你做了哪些调整,审核员是看得到的。
5. 常驻型扩展的状态管理与性能优化
5.1 跨标签页的状态同步怎么做才稳
常驻型扩展最核心的技术挑战,是让助手在多个标签页之间保持连贯。用户在 A 标签页问了一半的问题,切到 B 标签页继续,助手得记得上下文。这背后需要一套状态同步机制。
基础方案是用 chrome.storage 作为唯一数据源,所有标签页的 content script 都从 storage 读写状态,配合 chrome.storage.onChanged 监听变化。这样任何一个标签页更新了状态,其他标签页都能感知到。但要注意,storage 的写入是异步的,高频写入会有性能问题,需要做节流和批量合并。
进阶方案是在 background 里维护一个逻辑上的"会话状态",content script 只负责上报当前页面的局部信息,全局状态由 background 统一管理。这样职责更清晰,但 background 被挂起时状态会丢,所以关键状态还是要落 storage。
5.2 减少对页面性能的影响
用户对扩展的性能容忍度很低。一个常驻扩展如果让页面加载慢了几百毫秒,或者滚动时掉帧,用户会直接卸载。所以性能优化是常驻型扩展的必修课。
几个实操要点。content script 的注入时机尽量用 document_idle,避免阻塞页面渲染。页面扫描逻辑要做防抖,不要在 DOM 每次变动时都全量重扫,而是用 MutationObserver 监听关键区域。和 background 的通信要合并,不要每个小动作都发一条消息,攒一批再发。重计算逻辑尽量放到 background 或 offscreen document,别在主线程上跑。
还有一个容易被忽略的点是内存。content script 在每个标签页都有一份实例,如果每个实例都持有大量数据,标签页一多内存就爆了。所以 content script 里只保留当前页面必需的最小状态,其余的都放 background 或 storage。
5.3 用户感知层面的"轻"
技术上的轻量是一回事,用户感知上的轻是另一回事。一个常驻助手如果总是弹提示、总在动、总在刷存在感,用户会觉得烦。好的常驻扩展应该是"需要时在,不需要时隐形"。
具体做法包括:默认不主动弹窗,只在用户触发或明确需要时出现;动效克制,不要用夸张的动画抢注意力;提供清晰的开关,让用户能随时暂停助手;记住用户的偏好,不要每次都问同样的问题。这些细节看起来是产品设计,但实现上都依赖前面说的状态管理和性能优化做支撑。
6. 这类扩展后续能往哪些方向长
6.1 从单页助手到跨页工作流
现在的常驻助手大多还是单页思维,在当前页面帮你做点事。但真实的工作流往往是跨页的:你在 A 页收集信息,在 B 页整理,在 C 页输出。如果助手能理解这种跨页的意图,把多个标签页串成一条工作流,价值会大很多。
技术上这需要更强的会话管理和意图识别。扩展要能判断用户当前处于工作流的哪个阶段,主动把上一个阶段的结果带过来。这比单页助手复杂得多,但也是差异化最明显的地方。
6.2 与本地能力的结合
纯云端的助手有延迟和隐私顾虑。如果扩展能结合本地能力,比如本地的小模型、本地的文件处理、本地的剪贴板历史,很多场景的体验会更好。WebMCP 这类协议如果支持本地能力的注册,扩展就能把本地工具也纳入可调用的范围。
6.3 开放能力给第三方
一个扩展的能力终究有限。如果 EO2Weave 这类工具能把 WebMCP 的能力接口开放出来,让第三方开发者基于它开发针对特定网站或特定场景的增强,生态就能长起来。这需要一套清晰的协议和审核机制,但方向是值得的。
7. 我在实际折腾这类扩展时的一些体会
做浏览器扩展这些年,最大的感受是:技术难度往往不在最显眼的地方。OAuth 授权、WebMCP 协议这些听起来很唬人,但真正耗时间的,是 service worker 被挂起后状态丢失、是 content script 在某个特殊页面上注入失败、是审核员因为一句描述不清打回你的提交。这些琐碎的问题没有捷径,只能一个个踩过去。
另一个体会是,常驻型扩展的产品感和技术实现是深度绑定的。你不能先想一个炫酷的功能再去考虑性能,因为性能约束会直接决定哪些功能可行。反过来,你也不能只盯着性能做一个小透明工具,因为用户装你是为了解决问题。找到那个"能力足够有用、占用足够轻"的平衡点,是这类扩展成败的关键。
最后说个具体的。如果你也在做类似的扩展,建议尽早把状态管理这块设计清楚,别等到功能堆多了再回头重构。我见过太多项目,前期图快把状态散落在各个 content script 里,后期想加跨页能力时发现根本改不动。状态这层地基打好了,上面盖什么楼都稳。