【免费下载链接】opentabs
Browser automation clicks buttons. OpenTabs calls APIs.
OpenTabs 的定位是"Browser automation clicks buttons. OpenTabs calls APIs."——AI 不再去点按钮,而是直接通过你浏览器里已登录的会话调用真实网页 API。而要开发 OpenTabs 插件,核心就是本文要讲透的三件套:OpenTabsPlugin基类、defineTool工具工厂,以及onActivate / onDeactivate等生命周期钩子。读完这篇 OpenTabs 插件 SDK 核心参考,你就能独立写出并发布自己的 OpenTabs 插件。
一、SDK 是什么:一个插件的最小骨架
OpenTabs 插件 SDK 的包名是@opentabs-dev/plugin-sdk,它提供三样东西:
OpenTabsPlugin:所有插件必须继承的抽象基类(插件的"身份证 + 工具箱")defineTool:类型安全的工具定义工厂,让 AI Agent 通过 Zod Schema 调用你的 API 函数- 页面端工具集:DOM 等待、带会话 Cookie 的 fetch、存储、重试、日志等,全部运行在真实页面上下文里
核心 API 定义在 platform/plugin-sdk/src/index.ts,完整说明见 platform/plugin-sdk/CLAUDE.md。
二、OpenTabsPlugin:插件的必选与可选项
每个 OpenTabs 插件都是OpenTabsPlugin的子类。抽象类签名见 platform/plugin-sdk/src/index.ts#L77-L103。
必选字段(抽象属性/方法)
| 字段 | 类型 | 作用 |
|---|---|---|
name | string | 唯一标识,小写字母+连字符,会成为工具名前缀(如slack__send_message) |
displayName | string | 侧边面板显示名 |
description | string | 插件用途描述,展示给 MCP 客户端 |
urlPatterns | string[] | Chrome match pattern,决定哪些标签页会注入适配器 |
tools | ToolDefinition[] | 本插件的全部工具定义 |
isReady() | Promise<boolean> | 就绪探针:返回 true 表示用户已登录、服务可用 |
可选字段
| 字段 | 作用 |
|---|---|
excludePatterns | 排除与urlPatterns重叠但不想匹配的 URL |
homepage | 侧边面板"打开标签页"按钮跳转的具体 URL |
configSchema | 声明用户配置项(如自托管的 Instance URL),通过侧边面板或 config.json 填写 |
一个真实的最小示例是 Hacker News 插件,完整源码见 plugins/hackernews/src/index.ts:
class HackerNewsPlugin extends OpenTabsPlugin { readonly name = 'hackernews'; override readonly displayName = 'Hacker News'; readonly urlPatterns = ['*://news.ycombinator.com/*']; override readonly homepage = 'https://news.ycombinator.com'; readonly tools: ToolDefinition[] = [getItem, getUser, listTopStories /* ... */]; async isReady(): Promise<boolean> { try { await waitUntil(() => document.readyState === 'complete', { timeout: 5000 }); return true; } catch { return document.readyState === 'complete'; } } }💡 注意:脚手架生成的
tsconfig.json开启了noImplicitOverride,覆写父类成员必须加override关键字,否则构建时报 TS4114。
三、defineTool:定义一个 AI 可调用的类型安全工具
工具是插件与 AI Agent 对话的接口。defineTool本身是个恒等函数,没有运行时行为,它的价值在于TypeScript 泛型推断:输入输出都用 Zod Schema 声明,handle的参数和返回值自动获得类型。
ToolDefinition 字段速查
| 字段 | 说明 |
|---|---|
name | snake_case工具名,注册 MCP 时自动加插件名前缀 |
displayName | 侧边面板显示名,省略时由name自动生成 |
description | 展示给 AI Agent 的描述——这是 Agent 决定何时调用它的依据,要写得具体 |
summary/icon/group | UI 摘要、Lucide 图标、分组标题 |
input/output | Zod Schema,会序列化为 JSON Schema 供 MCP 注册与服务器端校验 |
handle(params, context?) | 异步处理函数,运行在真实页面上下文,输入已预校验 |
来自 Hacker News 插件的真实工具(plugins/hackernews/src/tools/list-top-stories.ts):
export const listTopStories = defineTool({ name: 'list_top_stories', description: 'Get the current top stories from the Hacker News front page.', input: z.object({ page: z.number().int().min(1).optional().describe('Page number (default 1)'), }), output: z.object({ stories: z.array(storySchema), has_more: z.boolean(), }), handle: async params => { const { stories, has_more } = await fetchStoryPage('/news', params.page ?? 1); return { stories: stories.map(mapStory), has_more }; }, });ToolHandlerContext:给长时间任务报告进度
handle的第二个参数context由适配器运行时注入,提供reportProgress({ progress, total, message }),用于长任务的分步进度提示(进度条会实时显示在侧边面板)。长任务建议始终用context?.reportProgress(...)可选链调用。
⚠️ Zod Schema 三条军规
工具 Schema 会被序列化为 JSON Schema(docs/content/docs/sdk/tools.mdx):
- 禁用
.transform()/.pipe()/.preprocess()——无法序列化,输入归一化逻辑放到handle里做 .refine()回调绝不能抛异常——即使前面的校验已失败,回调仍会执行- 修源头而不是序列化器:与 JSON Schema 冲突时,简化 Schema、把逻辑移入 handler
四、生命周期钩子完全参考:6 个可选方法一篇讲透
钩子让你在页面里"感知"适配器的状态变化,全部是可选方法,只实现你需要的那些即可。完整参考见 docs/content/docs/sdk/lifecycle-hooks.mdx,源码签名在 platform/plugin-sdk/src/index.ts#L125-L168。
| 钩子 | 触发时机 | 典型用途 |
|---|---|---|
onActivate() | 适配器注册成功后,仅一次 | 注册事件监听、MutationObserver 等页面级资源 |
onNavigate(url) | 页内 URL 变化(pushState/replaceState/popstate/hashchange) | SPA 路由切换时刷新状态 |
onToolInvocationStart(toolName) | 每次handle()执行前 | 埋点、前置检查 |
onToolInvocationEnd(toolName, success, durationMs) | 每次handle()结束后(无论成败) | 耗时统计、日志上报 |
onDeactivate() | 适配器被移除(插件更新/卸载/离开匹配页) | 清理onActivate创建的资源 |
teardown() | 插件更新重注入前,紧随onDeactivate之后 | 清理定时器、window全局等持久副作用 |
三个关键细节:
- 所有钩子运行在页面上下文;钩子内抛出的错误会被捕获并记录日志,绝不影响适配器注册或工具执行
- 标签页直接关闭时两个清理钩子都不会触发(浏览器先销毁了 JS 上下文)
- 钩子由
opentabs-plugin build生成的 IIFE 包装器自动接线,作者只需实现方法本身
经典"激活时注册、失活时清理"的配对写法:
override onActivate() { const handler = (e: KeyboardEvent) => { /* ... */ }; document.addEventListener('keydown', handler); this.cleanup = () => document.removeEventListener('keydown', handler); } override onDeactivate() { this.cleanup?.(); }五、加分项:ToolError 结构化错误与常用工具函数
结构化错误:让 AI 知道该不该重试
ToolError(platform/plugin-sdk/src/errors.ts#L19-L69)携带category、retryable、retryAfterMs元数据,一路透传到 MCP 服务器的结构化错误响应,AI Agent 可据此自动决策"重试还是放弃"。优先使用静态工厂方法:
| 工厂方法 | 分类 | 可重试 |
|---|---|---|
ToolError.auth(msg) | auth | ❌ |
ToolError.rateLimited(msg, retryAfterMs?) | rate_limit | ✅ |
ToolError.notFound(msg) | not_found | ❌ |
ToolError.timeout(msg) | timeout | ✅ |
ToolError.validation(msg)/ToolError.internal(msg) | validation/internal | ❌ |
页面端工具函数一览
SDK 还内置了跑在页面上下文的工具函数(platform/plugin-sdk/CLAUDE.md):
- DOM:
waitForSelector、observeDOM、getTextContent——基于 MutationObserver 等待元素 - Fetch:
fetchJSON、postJSON、fetchText等——自动携带页面会话 Cookie,并内置超时与限流头解析 - 存储:
getLocalStorage、getCookie、getAuthCache——安全读写页面存储 - 计时:
retry(指数退避重试)、sleep、waitUntil(轮询断言) - 日志:
log.info/warn/error——适配器运行时内直连 MCP 服务器日志流
六、延伸阅读:官方文档与源码路径
| 主题 | 位置 |
|---|---|
| 插件基类 API 参考 | docs/content/docs/sdk/plugin-class.mdx |
| defineTool 工具参考 | docs/content/docs/sdk/tools.mdx |
| 生命周期钩子参考 | docs/content/docs/sdk/lifecycle-hooks.mdx |
| 结构化错误类型 | docs/content/docs/sdk/error-types.mdx |
| 10 分钟写第一个插件 | docs/content/docs/first-plugin.mdx |
| SDK 源码入口 | platform/plugin-sdk/src/index.ts |
| 真实插件示例 | plugins/hackernews/src/ |
| 侧边面板权限管理 | assets/demo-todoist-permissions.gif |
掌握OpenTabsPlugin骨架 +defineTool工具定义 + 生命周期钩子这三个核心,再配合ToolError和页面工具函数,你就具备了开发任何 OpenTabs 插件的全部核心知识。用一条命令opentabs plugin create脚手架,写一个isReady和一个defineTool,你的 AI 就能通过浏览器登录态调用任意网页 API 了。
【免费下载链接】opentabs
Browser automation clicks buttons. OpenTabs calls APIs.
相关推荐
RoadRunner插件钩子:扩展服务器生命周期
RoadRunner插件钩子:扩展服务器生命周期 你是否曾想在PHP应用服务器启动时自动初始化资源,或在请求处理前后注入自定义逻辑?作为高性能PHP应用服务器,
后端Web服务器Livewire生命周期钩子:掌握组件生命周期的关键
Livewire生命周期钩子:掌握组件生命周期的关键 Livewire生命周期钩子是Laravel Livewire框架中至关重要的功能,它允许开发者在组件生命
后端前端Context Hub 文档精讲:AWS CodeDeploy JavaScript v3 SDK 部署管理与生命周期钩子实战
Context Hub 文档精讲:AWS CodeDeploy JavaScript v3 SDK 部署管理与生命周期钩子实战 本篇基于 Context Hub
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考