mcpField
P
poltertab

poltertab

本地服务
github.com/abhinav162浏览器自动化12 天前更新

关于 poltertab

PolterTab 是由本地 MCP 服务和 Chrome 扩展组成的工具,让 AI Agent 操作并读取已登录网站的现有浏览器配置。它提供页面交互、结构化抓取、网络捕获和标签页会话管理。

主要功能

  1. 01控制现有 Chrome 配置
  2. 02导航、点击、填写和滚动页面
  3. 03跨页面提取结构化记录
  4. 04捕获网络 JSON 响应
  5. 05通过命名会话管理标签页

适用场景

  1. 01自动处理已登录网站上的任务
  2. 02收集分页页面中的重复记录
  3. 03检查虚拟化页面界面背后的数据

MCP 相关

PolterTab 通过 stdio 连接 MCP 客户端,并由本地服务提供浏览器工具。服务通过本地 WebSocket 与 Chrome 扩展通信。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"abhinav162-poltertab": {
"command": "npx",
"args": [
"-y",
"poltertab"
]
}
}
}
启动命令npx -y poltertab

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmpoltertab1.12.0

工具

28

收录的工具列表 · 更新于 3 天前

参数类型必填说明
urlstring是
tabIdnumber否对该标签页(来自 browser_tab_list 的 ID)执行操作,但不将其设为当前标签页。省略则使用此 Agent 的当前标签页:最近导航、打开或切换到的标签页。Chrome 中不存在的 tabId 会返回 tab_not_found。
sessionstring否要运行的命名会话(标签页);它会成为当前标签页。使用新名称调用 browser_navigate 会在其自己的标签页中打开该会话,因此循环可以复用一个标签页,而不是打开数百个标签页。其他工具在该名称尚不存在时会报错。

详细介绍

PolterTab — 适用于任何 AI 的浏览器控制

让任何 AI Agent 使用你已登录的 Chrome。 PolterTab 是一个 Chrome 扩展程序和本地 MCP(Model Context Protocol)服务。

添加到 Chrome — 可在 Chrome 应用商店获取 npm 版本 npm 下载量 许可证

Chrome 扩展程序 + Model Context Protocol(MCP)服务。AI 可以在你已经使用的浏览器中操作:使用你的真实个人资料、Cookie 和已登录会话。安装扩展程序,将 MCP 客户端指向该服务即可——它兼容 Claude Desktop、Cursor 或任何兼容 MCP 的 Agent。

为什么选择 PolterTab

  • 你的真实个人资料 — 在你已登录的 Chrome 中运行。无需使用退出了所有账号的全新无头浏览器。
  • 无需复杂设置 — 从 Chrome 应用商店添加扩展程序,再添加一条 MCP 配置即可。
  • 适用于任何 AI 工具 — 任何兼容 MCP 的客户端都可以连接。

工作原理

Any MCP Agent (Claude Desktop, Cursor, custom agents)
       │  Stdio (MCP Protocol)
       ▼
Node.js MCP Server (mcp-server/index.js)
       │  WebSocket (port 7822 by default)
       ▼
PolterTab Chrome Extension
       │  Chrome APIs + DOM
       ▼
  Your existing Chrome profile

多个 Agent 可以同时指向同一个浏览器——最先占用 WebSocket 端口的服务会为扩展程序提供服务,其余服务则通过它进行代理。参见同时运行多个 Agent。

哪些内容会离开你的设备

PolterTab 完全在你的设备上运行。它没有 PolterTab 服务、账号或遥测功能。

  • MCP 服务在本地运行。 它通过 stdio 与 AI 客户端通信,扩展程序使用的 WebSocket 仅在本机监听(默认为 127.0.0.1 和 ::1,端口 7822)。该服务会拒绝来自网页的连接——浏览器连接必须来自 Chrome 扩展程序(chrome-extension://)源——而且只有 Chrome 应用商店版本(自动受信任)或你已配对的扩展程序,才能保持该连接、更改上传设置或读取日志。(架构)
  • 页面内容只会发送到你的 Agent 指定的位置。 PolterTab 会将读取或抓取的内容返回给 MCP 客户端;客户端会将其转发给你配置的模型提供商。PolterTab 本身不会将页面内容传送到任何地方。
  • 默认禁用文件上传,直到你允许某个文件夹。 在你使用 poltertab uploads allow <dir> 添加文件夹之前,browser_upload_file 会拒绝所有路径;敏感位置始终会被屏蔽,无法加入允许列表。运行 poltertab uploads 查看当前列表。
  • npm 溯源信息。 发布时使用 npm publish --provenance,因此 npm 会记录从已发布压缩包到生成该压缩包的 GitHub Actions 构建的签名且可验证的关联信息。
  • 一个例外——版本检查。 运行中的服务每天会发起一次外部请求,以检查 npm 版本(registry.npmjs.org);poltertab setup 还会一次性查询 Chrome 应用商店版本(clients2.google.com)。这两项检查只会比较版本,以便在服务与扩展程序版本不匹配时发出提醒——不会发送页面内容或个人数据。设置 POLTERTAB_NO_UPDATE_CHECK=1 即可关闭这两项检查。

从 1.12.0 开始, 只有 Chrome 应用商店扩展程序或你已配对的扩展程序才能控制浏览器;未打包安装的扩展程序可通过 poltertab uploads pair 配对一次。

如需报告漏洞,请参阅 SECURITY.md。

设置

需要 Node 18 或更高版本。

1. 运行安装程序
npm install -g poltertab
poltertab setup

向导会询问要为所有项目安装,还是仅为当前项目安装, 然后会完成以下三项设置:

  • browser-navigation-strategy 技能,让 Agent 知道如何操控会反抗的浏览器
  • CLAUDE.md 章节,让它知道这些工具的存在
  • 通过 claude mcp add 注册 MCP 服务

可以放心重新运行——每个步骤都会检测是否已执行过,因此升级或再次运行 都不会重复添加内容。

不想全局安装?npx poltertab setup 可以完成相同的操作。

2. 安装 Chrome 扩展程序

从 Chrome 网上应用店**添加到 Chrome**。Chrome 会自动让通过应用店安装的扩展程序保持最新, 因此,不开发扩展程序的用户都应通过此方式安装。

然后重启 AI 工具,以便加载新工具。

或者解压加载扩展程序(开发者/离线路径)

修改扩展程序或无法访问网上应用店时,请使用此方式。 解压加载的扩展程序不会自动更新;你需要自行从较新的 zip 文件重新加载。

  1. 下载最新的发布 zip 文件,并将其解压到单独的文件夹中(例如,运行 unzip poltertab-extension-vX.zip -d poltertab-extension,或在 macOS 上双击——系统会自动创建文件夹)
  2. 打开 Chrome → chrome://extensions/
  3. 启用开发者模式(右上角的开关)
  4. 点击加载已解压的扩展程序,并选择该文件夹(其中包含 manifest.json)
  5. 配对一次,让它能够控制浏览器:运行 poltertab uploads pair,然后输入扩展程序显示的 4 位代码。(通过应用店安装的扩展程序会自动受信任,无需此步骤。)
其他 MCP 客户端

此向导面向 Claude Code。其他任何 MCP 客户端也都可以使用——将 poltertab 注册为 stdio 命令:

{
  "mcpServers": {
    "poltertab": {
      "command": "poltertab"
    }
  }
}

使用 Cursor? 一键安装(添加上述 stdio 条目):

添加到 Cursor

如果从克隆仓库运行,而不是从 npm 运行,请让客户端直接指向该文件: {"command": "node", "args": ["/absolute/path/to/poltertab/mcp-server/index.js"]}

支持的 MCP 工具

服务向 LLM 提供 28 个工具(均以 browser_ 为前缀)。所有面向页面的工具 还可接受可选的 tabId 和 session,以指定目标标签页——两者都省略时, PolterTab 会使用 Agent 当前的标签页:即最近导航到、打开或切换到的标签页。 首次调用 browser_navigate(以及每个新会话中首次调用 browser_navigate)时,都会打开一个新标签页,而不是接管你正在查看的标签页。

页面操作
操作参数描述
browser_navigateurl(必填)导航到某个 URL
browser_clickselector(必填)点击元素(CSS/XPath/文本)
browser_fillselector、value(必填)、submit填写输入字段,可选择提交其表单
browser_hoverselector(必填)将鼠标悬停在元素上
browser_scrolldirection(必填)、amount、selector滚动页面或元素——up/down/left/right/top/bottom
browser_smart_scrolldirection(必填)、amount滚动,然后等待 2s,让延迟加载的网络数据到达
阅读页面

这里的每个工具都接受 output_file,可将负载写入 ~/.poltertab/downloads/,并且只返回摘要:行数、字段名称、填充率和两条示例记录。数据量超过一瞥所需时,请使用它;原始负载绝不应默认进入上下文窗口。

操作参数说明
browser_extractrecord, fields (required), anchor, max_text, probe, output_file提取重复记录,并按记录分组字段——见下文
browser_extract_allurl_template, record, fields (required), key, limit, offset, start_page, max_pages, fill_tolerance, output_file在一次调用中翻页并提取,无需每页与模型往返一次
browser_scrapeselector, attribute, multiple, fields, max_text, output_file抓取指定元素或整个页面。attribute 除任何属性名称外,也接受 text;href/src 会返回绝对路径。fields 用于选择完整页面抓取要返回的部分——["meta","jsonld"] 可获取 og: 标签和 schema.org 数据块,而无需返回 50KB 的正文文本
browser_snapshotinteractive_only, max_nodes, max_depth, output_file带引用标签的可见节点和交互节点树,最多 400 个节点
browser_get_textselector (required), max_text, output_file一个元素的文本内容。如果文本被截断,会在结果中标记
browser_get_title—页面标题和 URL
browser_get_url—页面 URL 和标题
browser_screenshot—当前可见标签页的 base64 PNG(会聚焦该标签页)
提取记录

browser_scrape 返回一个扁平列表,因此无法追踪每个值属于哪条记录。只有一个元素时这没问题,但有九十个元素时就很危险:只要有一个字段是可选的,按名称和电话号码进行扁平抓取,就会让后续所有电话号码上移一行,并导致页面其余数据错配。

browser_extract 接受一个记录容器,并在容器内部解析每个字段:

{
  "record": ".agent-card",
  "fields": {
    "name":    { "sel": "a.agent-card-name", "get": "text" },
    "url":     { "sel": "a.agent-card-name", "get": "href" },
    "phone":   { "sel": "a[href^='tel:']",   "get": "href", "strip": "tel:" },
    "socials": { "sel": "a.social-button",   "get": "href", "many": true }
  },
  "anchor": "url",
  "output_file": "agents.csv"
}
  • 缺失字段会返回 null。 绝不会导致数据错位。
  • sel 相对于记录根节点;省略它(或使用 ".")即表示根节点本身。
  • get 可以是 text(默认值)、href、src 或任何属性名称。
  • anchor 指定一个真实记录必定包含的字段。缺少该字段的记录——例如列表最后一页用于填充版面的占位卡片——会被丢弃,并计入 dropped,而不会作为全是 null 的行输出。

结果会报告执行情况,因此空列不会被误认为有效答案:

{ "count": 12, "dropped": 0,
  "fill_rates": { "name": 12, "phone": 10, "socials": 0 },
  "warnings": ["socials: 0/12 within record scope, but 40 matches page-wide
                for a.social-button — record boundary likely too narrow"] }

该警告表示启用了放宽探测。任何返回结果完全为空的字段都会在整个页面范围内重新检查,因为看起来像记录的容器往往不包含某些字段所在的同级元素;而“0 个社交账号”读起来像是“这些人没有社交账号”,而不是“你的选择器范围有误”。

browser_extract_all 接受相同的规范,外加一个包含 {page} 的 url_template,并自行执行循环。它会按 key 去重,并在以下任一条件首次出现时停止:limit_reached、empty_page、duplicate_page(某页的记录与更早页面重复——这是网站忽略无法识别的页面大小参数时的表现)、fill_rate_deviation(某页的填充率相较于第一页的基准骤降,这也是在第 1 页学到的规范悄然退化到第 10 页时的表现),或 max_pages。它始终会报告触发的条件,并返回截至该时点收集的所有内容,因此停止不会让你白跑一趟。

.csv 和 .jsonl 输出文件会以对应格式写入。

网络捕获

对于 DOM 经过混淆或虚拟化的网站,应读取驱动 UI 的 JSON,而不是抓取可能不存在的元素。

操作参数说明
browser_set_intercept_patternspatterns(必填)要捕获的 URL 子字符串,例如 ["graphql", "/api/v1/"]。默认为 ["graphql", "/api/", "voyager", "feed"]
browser_get_network_stateclear、output_file捕获该标签页的原始 JSON。output_file 会将内容写入 ~/.poltertab/downloads/,并且只返回路径,避免大型负载占用上下文窗口
会话

会话是一个有名称的标签页。为每个 Agent 分配自己的会话,可避免多个 Agent 同时操作一个标签页;关闭的标签页会在下次使用时根据存储的 URL 重新创建。

操作参数说明
browser_session_createname(必填)、url、tabId创建会话、接管已打开的标签页,或将会话绑定到当前标签页
browser_session_switchname(必填)激活会话;如果其标签页已关闭,则恢复该标签页
browser_session_list—列出所有会话,每个会话的状态为 alive、recoverable 或 expired
browser_session_closename(必填)、force关闭会话及其标签页。被接管的标签页会返回 needs_user_permission;征求用户同意后,使用 force: true 重试。
browser_session_context—当前标签页的 ID、URL、标题和会话,或 closedTabId
标签页

一个 Agent 可以操作多个标签页。每个 Agent(以及每个辅助服务)都有自己的当前标签页;未指定 tabId/session 的面向页面工具会在该标签页上执行。指定 tabId 时,工具会操作对应的标签页,而不会切换当前标签页;如果 Chrome 中不存在该 tabId,任何接受该参数的工具都会返回 tab_not_found。如果当前标签页已关闭,未指定目标的调用会显示一条指出已关闭标签页的消息并失败,而不会悄悄转而操作其他标签页。

操作参数说明
browser_tab_newurl、session、background打开一个标签页并将其设为当前标签页
browser_tab_listall列出 PolterTab 的标签页(或所有标签页),并标记当前标签页
browser_tab_switchtabId 或 session、focus将另一个标签页设为当前标签页。如果 Chrome 中不存在该标签页,则返回 tab_not_found。
browser_tab_closetabId 或 session、force关闭一个标签页(默认为当前标签页)。由 PolterTab 以外的方式打开的标签页(包括通过 browser_session_create({tabId}) 接管的标签页)会返回 needs_user_permission——征求用户同意后,使用 force: true 重试。如果 Chrome 中不存在该标签页,则返回 tab_not_found。
文件上传
操作参数说明
browser_upload_fileselector(必填)、paths(必填)、method("picker" 或 "drop")、tabId、session将本地文件上传到页面。method: "picker"(默认)会点击 selector,并通过 CDP 拦截操作系统的文件选择器——适用于任何 <input type="file"> 或会打开文件选择器的按钮。method: "drop" 会触发 DataTransfer 拖放事件——适用于拖放区域。路径必须位于 ~/.poltertab/config.json 中 upload_allowed_paths 列出的目录内。使用文件选择器上传期间会短暂显示调试横幅(CDP 要求)。

配置——使用 browser_upload_file 前,将 upload_allowed_paths 添加到 ~/.poltertab/config.json:

{
  "upload_allowed_paths": ["/Users/you/Documents", "/Users/you/Downloads"]
}

无论是否在允许列表中,以下路径始终被阻止:~/.ssh、~/.aws、~/.gnupg、~/.config、/etc、/private/etc、/System、/private/System、/Library、/private/Library、/proc、主目录中的点文件。注意:/tmp(及其 macOS 真实路径 /private/tmp)不会被阻止,可以正常添加到 upload_allowed_paths 中。

站点记忆
操作参数描述
browser_get_site_memoryhostname(或 domain)记录的域名障碍及解决方法
browser_save_site_memoryhostname(或 domain)、obstacle、solution记录网站遇到的问题及解决方法
选择器解析

选择器按以下顺序解析:

  1. 快照引用 — @e12,由 browser_snapshot 返回。解析为快照所描述的确切节点,优先于生成的类名链。
  2. CSS 选择器 — #id、.class、div > span
  3. XPath — //div[@class="foo"]
  4. 文本匹配 — 元素的精确文本内容

如果以上四种方式都未匹配到,则会重试,并穿透 Shadow DOM 根节点(开放根节点,深度为一层)和搜索顶层同源 iframe。不支持嵌套 iframe(iframe 内含 iframe)和跨域 iframe;在这些情况下,工具会返回明确的错误。

故障排除与选项

同时运行多个 Agent: 第一个绑定端口 7822 的 MCP 服务会成为主服务,并负责与扩展程序建立连接。之后启动的服务发现端口已被占用后,会切换为次级模式,通过主服务代理命令,因此多个 Agent 无需额外配置即可共享一个浏览器。主服务会为每个次级服务的命令附上其 ID,扩展程序会为每个 Agent 分别保留当前标签页,因此各 Agent 都有自己的标签页。最多接受 POLTERTAB_MAX_SECONDARIES 个 v2 次级服务(默认值为 50);如果主服务退出,其中一个次级服务会晋升并接替它。扩展程序弹出窗口会列出所有已连接的 Agent。

一个浏览器可供多少个 Agent 共享: 最多可由 50 个 v2 次级服务(每个进程一个)和主服务共享一个浏览器。通过 POLTERTAB_MAX_SECONDARIES 提高或降低上限。如果某个 Agent 闲置达到 POLTERTAB_IDLE_EVICTION_MS(默认 5 分钟),则会以关闭代码 4001 将其逐出,以释放名额;它下次发送命令时会自动重新连接,因此 Agent 不会察觉此次逐出。主版本与主服务不匹配的 Agent 会以关闭代码 4002 被拒绝——请参阅下文的升级路径。

命令队列: 主服务会限制同时发送给扩展程序的命令数量(POLTERTAB_MAX_CONCURRENCY,默认值为 6)。达到上限时,v2 次级服务的命令会按次级服务分别排队(最多 POLTERTAB_QUEUE_DEPTH 个名额,默认值为 3)。排队命令会立即收到 {"type":"queued","id":"...","position":N} 确认,让调用方知道命令正在等待处理,而非已丢失。v2 次级服务在收到此确认时,以及命令离开队列时收到 {"type":"dispatched","id":"..."} 后,都会重新启动 35 s 超时计时器,因此队列等待时间不会占用执行时限。如果某条命令到达时该次级服务的队列已满,则会立即以 [queue_full action=X] 拒绝。主服务在相同上限下也会为自己的直接命令排队。调用方的 35 s 时限即将耗尽时,排队命令会被丢弃,并返回 [queue_timeout action=X](预留 2 s 缓冲时间,以确保丢弃发生在调用方自身计时器超时之前)。POLTERTAB_QUEUE_TIMEOUT_MS(默认值为 60 s)是针对始终未出队的队列命令设置的独立垃圾回收兜底机制,不会延长队列等待时间。

/status 端点: GET http://127.0.0.1:7822/status 会返回主服务队列状态、已连接 Agent 和等待时间百分位数(p50/p95/p99)的 JSON 快照。该端点仅允许回环地址(127.0.0.1、::1 或 ::ffff:127.0.0.1)的调用方访问,且请求的 Host 标头必须是 localhost 或回环地址;其他请求均会收到 404。 扩展重连: 桥接服务重启或 WebSocket 断开时,Chrome 扩展会自动重新连接。断开后会立即尝试连接,然后按 1 s、2 s、4 s……的间隔逐渐延长,最长为 30 s;如果服务工作线程在计划执行过程中被挂起,闹钟会唤醒它。重连期间,命令会返回:

  • [extension_reconnecting port=7822 elapsed=Xs] — 扩展已断开,正在重新连接。请稍后重试。
  • [extension_never_connected port=7822] — 扩展从未连接到此服务实例。请检查扩展是否已安装、启用,并且指向正确的端口。

关闭代码:

代码含义Agent 行为
4001空闲时逐出 — Agent 在 POLTERTAB_IDLE_EVICTION_MS 指定的时间内没有活动次级实例会在下次收到命令时延迟重新拨号;无需采取操作
4002版本不匹配 — 次级实例的主版本与主实例不兼容次级实例会停止重试,直至重启;请更新 npm 包,确保每个会话运行相同的主版本

升级路径(1.x → 1.8): PolterTab 1.8 引入了版本握手。主版本与主实例不匹配的次级实例会收到关闭代码 4002 并停止重试;更新 npm 包(确保所有会话运行相同的主版本)即可解决问题。同时也应更新 Chrome 扩展,以获得 PR-4 的重连行为。升级前请运行 poltertab doctor 检查版本是否不一致。尚未升级的 1.7.x 次级实例不会收到 4002(它不支持握手),并会继续作为未标记版本的旧版次级实例运行;它会绕过命令队列,且永远不会因空闲而被逐出。

环境变量:

变量默认值说明
POLTERTAB_MAX_SECONDARIES50最多接受的 v2 次级实例数量
POLTERTAB_IDLE_EVICTION_MS300000 (5 min)v2 次级实例被以 4001 逐出前的空闲时长
POLTERTAB_MAX_CONCURRENCY6扩展命令并发限制
POLTERTAB_QUEUE_DEPTH3每个次级实例在返回 [queue_full] 前的队列深度
POLTERTAB_QUEUE_TIMEOUT_MS60000 (60 s)卡住的排队命令的 GC 兜底超时(不会延长队列等待时间)
POLTERTAB_COMMAND_TIMEOUT_MS35000 (35 s)每条命令的时间预算。必须大于扩展的 30 s 导航等待时间加 500 ms 稳定时间;仅针对测试调整

更改 WebSocket 端口: 仅当占用 7822 的是 并非 PolterTab 的其他程序时,才需要更改端口——PolterTab 服务按设计共享该端口(见上文)。两端都必须更改:

  1. 点击 Chrome 中的扩展图标 -> 设置。

  2. 将 WebSocket 端口更改为一个空闲端口(例如 7824),然后点击 保存 — 扩展会立即重新连接,无需重新加载。

  3. 更新 MCP 服务配置,传入新端口。

    • 使用环境变量:MCP_BROWSER_WS_PORT=7824 node index.js
    • 使用 CLI 参数:node index.js --port 7824
    • 在 Claude Desktop 配置中:
    "args": ["/absolute/path/to/poltertab/mcp-server/index.js", "--port", "7824"]
    

DevTools 打开时选择器上传失败: Chrome 每个标签页一次只能启用一个调试器。当目标标签页打开 DevTools 时,browser_upload_file(方法 "picker")会失败并显示“Another debugger is already attached”。运行选择器上传前,请关闭该标签页上的 DevTools。

添加 debugger 权限后需要重新批准扩展: 添加 debugger 权限后加载或重新加载扩展文件夹时,Chrome 会提示你重新批准 — 在 chrome://extensions/ 的警告对话框中点击 保留 或 允许 之前,扩展会处于停用状态。以解压方式加载的扩展不会自动更新,因此只有在你明确重新加载或更新扩展文件夹时才会发生这种情况。

扩展未连接:

  • 确保 MCP 服务正在运行,并且指向正确的端口。
  • 从 chrome://extensions/ 重新加载扩展

命令超时 / 受限页面:

  • 确保 Chrome 中有一个活动标签页。
  • Chrome 扩展无法与 chrome:// URL、Chrome 网上应用店或某些受限页面交互。在这些情况下,MCP 服务会向 Agent 清晰地报告“无法与此页面交互”。

项目结构

poltertab/
├── package.json               # Published npm package (deps + bin)
├── bin/
│   ├── poltertab.js           # Entry: bare = MCP server, `setup` = wizard
│   └── setup.js               # Installer (skill, CLAUDE.md, MCP registration)
├── skills/
│   └── browser-navigation-strategy/   # Shipped skill, installed by the wizard
├── assets/
│   └── claude-md-snippet.md   # The CLAUDE.md section the wizard appends
├── mcp-server/
│   ├── index.js               # MCP server (stdio) + WebSocket hub
│   └── test/run.js            # Extension + server regression suite
├── test/
│   └── setup.test.js          # Installer regression suite
├── chrome-extension/
│   ├── manifest.json          # MV3 manifest
│   ├── background.js          # WebSocket client, session manager, command router
│   ├── content_script.js      # DOM extractor + action executor (isolated world)
│   ├── interceptor.js         # fetch/XHR capture (main world)
│   ├── popup.html/js          # Connected-agents UI
│   ├── options.html/js        # Port configuration UI
│   └── icons/
└── README.md

保持更新

poltertab doctor

此命令会报告已安装的版本、npm 是否有更新版本,以及——最重要的是——两个部分的版本是否一致:

  server      1.1.0
  extension   1.0.0  SKEW      last seen 3m ago
  bridge      listening on port 7822
  state       ~/.poltertab
  npm         1.2.0 available

运行 npm update -g poltertab 时,npm 包会更新。从 Chrome 网上应用店安装的扩展会像其他网上应用店扩展一样自动更新。已加载的解压扩展绝不会自行更新——Chrome 只会自动更新来自网上应用店的扩展。因此,真正需要关注的问题不是缺少更新,而是版本不一致:较新的服务发送了较旧扩展尚未实现的命令(或相反,Chrome 已更新扩展,但你还没有运行 npm update),这会导致元素莫名其妙地无法找到。

两个部分使用同一个版本号,因此版本不一致一目了然。扩展会在连接时报告自己的版本;服务会进行比较、记录日志,并在扩展弹出窗口中显示结果。如果任一部分版本落后,会话中的第一个工具响应会提示一次,让你及时得知,无需特意记得去检查。

更新检查每天最多访问一次 registry.npmjs.org,结果会缓存在 ~/.poltertab/update-check.json 中,且绝不会阻塞任何操作。设置 POLTERTAB_NO_UPDATE_CHECK=1 可关闭更新检查——版本不一致检测在本地进行,无论是否关闭更新检查都会继续工作。

发现版本不一致时,poltertab doctor 会以非零状态码退出,以便 CI 或 Shell 提示符据此采取操作。

数据存储位置

服务积累的所有内容都不会存储在包内:

~/.poltertab/
├── navigation_memory/   # Per-domain obstacles and fixes (browser_save_site_memory)
└── downloads/           # output_file payloads from any read tool

对于全局 npm 安装而言,这一点很重要。包位于 node_modules/ 中,因此写在代码旁边的任何内容都会在下次运行 npm update -g 时被删除——每次升级后,站点记忆都会像失忆一样,而抓取的负载则会被埋在你永远不会去找的地方。

设置 POLTERTAB_HOME 可迁移整个目录树。如果你正在从将记忆保存在 mcp-server/navigation_memory/ 的版本升级,那么首次启动时会将其复制过来;如果目标位置已有笔记,则始终以现有笔记为准。

发布

发布由 GitHub Releases 驱动。npm 发布渠道由版本字符串决定,因此只需确保 Git 标签正确:

版本npm 标签安装方式
1.2.0latestnpm install -g poltertab
1.2.0-rc.1rcnpm install -g poltertab@rc
1.2.0-beta.1betanpm install -g poltertab@beta
1.2.0-alpha.1alphanpm install -g poltertab@alpha

发布一个版本:

npm version 1.2.0-beta.1     # bumps package.json, syncs the manifest, tags
git push --follow-tags
gh release create v1.2.0-beta.1 --generate-notes --prerelease

npm version 会运行 scripts/sync-manifest-version.js,确保 chrome-extension/manifest.json 中的版本同步。两个文件不能使用相同的字符串——Chrome 会拒绝带有预发布标识符的清单版本——因此,数字基础版本写入 version,完整字符串写入 version_name。

随后发布该版本会运行工作流:执行测试、检查版本一致性、在解析出的标签上运行 npm publish --provenance,并构建扩展 ZIP 文件并将其附加到发布中。

检查程序会拒绝发布,而不会自行猜测。如果标签与 package.json 不一致、清单文件版本落后,或稳定版本被标记为预发布版本,整个流程都会失败——最后一种情况之所以会失败,是因为否则该版本会进入 latest,并成为所有人的默认安装版本。

使用工作流的手动触发器(dry_run: true),即可演练整个流程而不实际发布。

测试

npm install
npm test

无需框架和浏览器。共有两个测试套件:

  • mcp-server/test/run.js — 内容脚本注入幂等性、穿透 Shadow DOM、跨框架搜索、标签页导航加载竞态,以及 MCP 服务端到端测试。它会在端口 7931 上针对一个虚拟扩展启动真实的服务进程,因此不会干扰已在 7822 上运行的 PolterTab。
  • test/setup.test.js — 安装程序:作用域解析、技能安装,以及 CLAUDE.md 幂等性。每个用例都会在一次性临时目录中运行,因此泄漏的写入不会影响你真实的 ~/.claude。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。