把本地 Kimi WebBridge 封装成 MCP 工具,让任意客户端操控真实浏览器
28.6/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/31 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add LosEcher/kimi-webbridge-mcp该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · browser
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 10 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查要求 Node:>=18(dsh 本身要求 Node ≥ 22.19)本插件尚未跑静态安装检查,以上为仓库状态警告。
>=18(dsh 本身要求 Node ≥ 22.19)最后检查 2026/10/31 22:51:29
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 10 处,展示前 10 条)
- 发起网络请求lib/daemon.mjs:90
const res = await fetch(`${cfg.daemonUrl}/command`, { - 执行外部命令来自配置/变量lib/daemon.mjs:160
spawn(cfg.daemonBin, ['start'], { detached: true, stdio: 'ignore' }).unref() - 执行外部命令来自配置/变量lib/daemon.mjs:175
const child = spawn(cfg.daemonBin, ['status'], { stdio: ['ignore', 'pipe', 'pipe'] }) - 读取本地文件lib/obs.mjs:18
const SNAPSHOT_SRC = readFileSync(new URL('./snapshot.page.js', import.meta.url), 'utf8') - 读取本地文件lib/act.mjs:23
const CHECK_SRC = readFileSync(new URL('./check.page.js', import.meta.url), 'utf8') - 执行外部命令固定命令lib/tabs.mjs:60
const url = execFileSync('osascript', ['-e', script], { encoding: 'utf8', timeout: timeoutMs }).trim() - 执行外部命令固定命令extras/open-tab.mjs:44
const res = execFileSync('curl', ['-s', '-m', String(timeoutMs), '-X', 'POST', DAEMON, '-H', 'Content-Type: application/json', '-d', JSON.stringify({ - 执行外部命令固定命令extras/open-tab.mjs:65
const probe = execFileSync('osascript', ['-e', 'tell application "Vivaldi" to get URL of active tab of window 1'], { encoding: 'utf8' }).trim() - 执行外部命令固定命令extras/vivaldi-open.mjs:32
return execFileSync('osascript', ['-e', script], { encoding: 'utf8' }).trim() - 执行外部命令固定命令extras/vivaldi-open.mjs:36
const res = execFileSync('curl', ['-s', '-m', String(timeoutMs), '-X', 'POST', DAEMON, '-H', 'Content-Type: application/json', '-d', JSON.stringify({
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 封装为MCP工具
- 暴露浏览器控制能力
- 供任意MCP客户端调用
MCP stdio 服务器,把本地 Kimi WebBridge 守护进程暴露成浏览器控制工具——供 DSH、Claude Code、Codex 及任意 MCP 客户端使用
README
由 DeepSeek 最新模型翻译生成kimi-webbridge-mcp
把本地 Kimi WebBridge daemon(http://127.0.0.1:10086)包装成标准 MCP stdio server,让任何 MCP 客户端都能用真实浏览器工具:DSH(@deepseek-ai/dsh-mcp-client)、Claude Code、Codex 等。
零依赖:Node ≥ 18 直接运行,新行分隔 JSON-RPC 2.0 over stdio。
Kimi 官方 kimi-webbridge install-skill 只会把 skill 装进 Claude Code / Codex / Kimi CLI / Hermes,不装 DSH。本仓库补上 DSH(及任意 MCP 客户端)这一环,且工具带 JSON Schema,优于纯文本 skill 调用。
v1.1.0:守卫化快环(obs / act / open_tab)
这一版补上三件事,用来修「agent 说点了、也报成功、页面没动」和「navigate 挂死」这两个真实痛点:
| 新工具 | 做什么 |
| obs | 一次页内读取的原子观测:索引元素表(ref/kind/role/name/value)+ 可见文本 + 滚动状态,模型面载荷有字符预算(默认 4000,超出显式记 omitted/truncated)。
展开完整文档(README 剩余 1 节)
kimi-webbridge-mcp
把本地 Kimi WebBridge daemon(http://127.0.0.1:10086)包装成标准 MCP stdio server,让任何 MCP 客户端都能用真实浏览器工具:DSH(@deepseek-ai/dsh-mcp-client)、Claude Code、Codex 等。
零依赖:Node ≥ 18 直接运行,新行分隔 JSON-RPC 2.0 over stdio。
Kimi 官方 kimi-webbridge install-skill 只会把 skill 装进 Claude Code / Codex / Kimi CLI / Hermes,不装 DSH。本仓库补上 DSH(及任意 MCP 客户端)这一环,且工具带 JSON Schema,优于纯文本 skill 调用。
v1.1.0:守卫化快环(obs / act / open_tab)
这一版补上三件事,用来修「agent 说点了、也报成功、页面没动」和「navigate 挂死」这两个真实痛点:
| 新工具 | 做什么 |
| obs | 一次页内读取的原子观测:索引元素表(ref/kind/role/name/value)+ 可见文本 + 滚动状态,模型面载荷有字符预算(默认 4000,超出显式记 omitted/truncated)。替代 snapshot(AX 树文本,实测 p50 45.5k 字符 ≈ 11k tokens,真实页还会截断)。 |
| act | 守卫化执行:先一次页内复核(还在 DOM 里?未禁用?可见?在视口内?未被遮挡?role/name/value/周围文本没变?),不匹配就拒绝且一个事件都不派发;匹配才走真 Input.(dispatchMouseEvent / insertText / dispatchKeyEvent),并校验事件真的落在目标元素上。几何在 act 时重新解析——元素只是移动了照样能点到。 |
| open_tab | 不依赖 navigate 的标签页生命周期:复用会话已有标签 → 在其中 cdp Page.navigate → 借用用户已经打开的页 → 最后才「一次性」尝试后台建标签;每步都带客户端超时预算,失败不盲目重试。 |
ref 是身份 + 代号的不可复用字符串(形如 o3:17),不是位置编号:旧 ref 要么仍指向同一个节点(且含义未变才放行),要么明确失败(detached / node gone)。
对比扩展自带 @e1:它按位置复用,跨快照使用会静默点到别的元素并返回 success:true。
实测数字(2026-09-18,本机 Vivaldi + 扩展 2.0.13 / daemon v2.0.15)
真机实测(test/e2e-live.mjs 跑通的部分):
| 项 | 数值 |
| obs 载荷 / 往返 | 3729 字符,1 次页内读取,22–26 ms(同页 AX 树为 45.5k 字符) |
| act 干净点击 | 85 ms,delivered:true,落在目标 BUTTON#go 上,页面状态 idle → searched,__clicks = {any:1, trusted:1, dom:0} |
| act 语义漂移(改名后点旧 ref) | 拒绝,零输入:reason_code:"semantic_drift"、no_input_dispatched:true、__clicks.any 保持 0 |
| act 目标位移 300–500 px | 照样命中:观测时中心 y=258 → 实际派发 y=557,命中原元素 |
| act 元素被移除 / 页面重载 | 拒绝(detached / 页面身份变化),零输入 |
| 后台标签保活 | 真·后台标签(document.hidden:true)rAF 0/s → 120/s(Emulation.setFocusEmulationEnabled 开/关同标签对照) |
| open_tab 建标签(cdp) | 创建 9–21 ms,整轮 432–460 ms;用户可见标签不变 |
navigate 为什么被绕开(同一实测,代码级定位):
- 扩展的就绪判定是精确 URL 相等(tab.url !== requested && tab.url !== requested + '/'),任何重定向/归一化都要走满 30 s;
- 当前版本更糟:30 s 计时器里包着一次 300 s 超时的 CDP 调用 → 表现为无界挂死(curl -m 200 连 502 信封都收不到),且 不创建标签;
- 实测:navigate 3/3 抢焦点但不绑定会话;Target.createTarget 创建后是否被会话收养只有 1/3–2/3(扩展只在 Chrome 盖上 openerTabId 时经 /internal/tab-adopted 收养,cdp 是无白名单盲转发,没有确定性绑定)。
因此 open_tab 的可靠性来自「不新建标签」:复用会话标签 + cdp Page.navigate + 自己轮询 document.readyState/URL 就绪,只在会话一个标签都没有时才建一次;建失败会如实报 created:true, adopted:false, orphan_warning:true。
会话信任守卫(重要安全行为)
标签绑定是显式记账的。open_tab 在借用用户标签当 CDP 锚之后若没能绑定自己的标签, 会话会被标记为不可信,此后 obs/act 直接拒绝(reason_code:"session_unbound",且不读取任何页面),直到有一次成功的绑定(open_tab / find_tab / navigate)。close_tab 之后同样会失效。
需要故意操作「当前是什么标签就操作什么」时传 allow_any_tab:true 显式覆盖。
这条不是洁癖:真机实测过一次 open_tab 建标签失败后,会话的工作标签停在借用的用户标签上,下一次 obs 读到的就是用户正在用的内部业务系统页面。守卫让这种状态 fail-closed。
已知限制(诚实清单)
- 建标签不可靠:扩展没有确定性绑定 API(Target.activateTarget/getTargets 均被拒 -32000 Not allowed)。
建失败会在浏览器里留下孤儿标签:它不在任何会话里,find_tab/close_tab 都够不到,只能手动关。
所以本 wrapper 一次只建一次、绝不重试;open_tab 会明确告诉你可能留下了哪个 URL 的标签。
- ref 的语义新鲜度是启发式:pageKey(导航身份 + 视口)+ 每节点 guard(含作用域文本)变化即拒绝。
作用域文本变化可能来自无关区域 → 会误拒(误拒是安全的:act 会附带一次新观测让你重决策)。
- obs 覆盖不到的地方:iframe / shadow DOM / canvas / 上传 / 新窗口 —— 这些继续用 snapshot + click/fill。
- obs 只列视口内元素(和 jev 一致):需要页面下方的元素时先滚动再 obs。
- Page.setWebLifecycleState 只是 best-effort 兜底,失败不影响 keep_alive.focus_emulation。
快速开始
1. daemon 就绪?(没有会自动拉起) ~/.kimi-webbridge/bin/kimi-webbridge status
2. 浏览器无关门禁(语法 + 单测 + 假 daemon 走完整 MCP stdio) bash test/gates.sh
3. 真机端到端(需扩展在线;会开 1 个后台标签,跑完自动清场) bash test/gates.sh --live
4. 任意 MCP 客户端指向这个命令即可:
node path/to/kimi-webbridge-mcp/server.mjs # 或安装后直接 webbridge-mcp
bash test/gates.sh 只跑不碰浏览器的门禁:node --check 全部源文件 + fixture 内联脚本、 node --test test/unit.test.mjs(41 例)、node test/mcp-smoke.mjs --mock(假 daemon 走完整 MCP 协议)。
真机 E2E 默认显式跳过并打印,不会静默算通过;--live 才跑,无扩展时退出码 3(SKIP)。
接入 DSH
安装 bundle 后用自带的 overlay 补丁(@deepseek-ai/dsh-mcp-client 插件,stdio transport):
dsh plugin --profile web add github:LosEcher/kimi-webbridge-mcp#main
dsh web --patch <path/to/dsh-webbridge.cordis.yml>
工具以 mcp__webbridge__<name> 出现在模型面前(如 mcp__webbridge__obs)。
想永久启用,把 dsh-webbridge.cordis.yml 里的 insert 合并进 $DSH_HOME/cordis.patch.yml (或对应 profile 的 cordis.patch.yml)。
升级后让 DSH 看到新工具:不需要重启宿主。MCP 客户端带重连策略(默认 enabled:true、 500 ms 起、10 次),杀掉旧的 server.mjs 子进程后客户端会重连并重新注册工具;也可以直接重启宿主。装的是 github: 依赖时注意 pnpm update kimi-webbridge-mcp 才会拉到新提交。
工具
快环(优先用这三个)
| MCP 工具 | 说明 | 关键参数 |
| obs | 一次页内读取的原子观测:索引元素表 + 可见文本 + 滚动状态 | max_chars(4000)、max_elements(120)、text_chars(1200)、include_rect、keep_alive |
| act | 守卫化执行:复核不通过即拒绝且零输入;通过则走真 Input. 并校验送达 | ref、kind(click/fill/press/select)、text、key、value、settle_ms(40)、verify_delivery、refresh |
| open_tab | 不依赖 navigate 的标签页生命周期:复用 → 会话内换页 → 借用 → 一次性建标签 | url、reuse、can_create、ready_timeout_ms(5000)、anchor |
完整面(逃生通道,语义与 1.0 一致)
| MCP 工具 | 说明 | 关键参数 |
| navigate | 打开 URL(真实浏览器)。可能无界挂死,已被预算包住并提示改用 open_tab | url、newTab、group_title |
| find_tab | 重选本会话打开的标签页;active:true 借用用户正在看的页 | url、active |
| snapshot | 当前页无障碍树(文本),返回 @e 引用(位置化,勿跨快照复用) | — |
| click | 点击元素(@e 引用或 CSS,无守卫) | selector* |
| fill | 填输入框/textarea/contenteditable(clear-and-insert,无守卫) | selector、value |
| evaluate | 页内执行 JS(支持 async) | code* |
| cdp | chrome.debugger 原始 CDP 透传(逃生通道;要求会话已有标签) | method、params |
| screenshot | 截图(整页或元素),返回本地文件路径 | format、quality、selector、path |
| network | 网络活动采集/查看 | cmd(start/stop/list/detail)、filter、requestId |
| upload | 上传文件到 | selector、files |
| save_as_pdf | 当前页存 PDF,返回本地路径 | paper_format、landscape、scale、print_background、path |
| list_tabs | 列出会话内标签页 | — |
| close_tab | 关闭当前(工作)标签页 | — |
| close_session | 关闭会话全部标签页(含借用的用户标签,仅用户明确要求时调用) | — |
| webbridge_status | daemon/扩展状态(走 kimi-webbridge status CLI) | — |
所有工具都接受可选 session 参数:一个任务 = 一个 session = 一个标签组,同一任务的所有调用传同一个 session(缺省 webbridge-mcp)。
环境变量
| 变量 | 默认 | 说明 |
| WEBBRIDGE_DAEMON_URL | http://127.0.0.1:10086 | daemon 地址 |
| WEBBRIDGE_DAEMON_BIN | ~/.kimi-webbridge/bin/kimi-webbridge | 自动拉起/状态查询用的 CLI |
| WEBBRIDGE_MCP_TIMEOUT_MS | 60000 | 单次调用缺省预算 |
| WEBBRIDGE_MCP_OBS_TIMEOUT_MS | 15000 | obs 页内读取预算 |
| WEBBRIDGE_MCP_ACT_TIMEOUT_MS | 10000 | act 前置复核预算 |
| WEBBRIDGE_MCP_ACT_INPUT_TIMEOUT_MS | 8000 | act 输入/校验预算 |
| WEBBRIDGE_MCP_OBS_MAX_CHARS | 4000 | obs 模型面字符预算 |
| WEBBRIDGE_MCP_OBS_MAX_ELEMENTS | 120 | obs 元素上限 |
| WEBBRIDGE_MCP_OBS_TEXT_CHARS | 1200 | obs 可见文本上限 |
| WEBBRIDGE_MCP_AUTOSTART | 1 | 连接失败时自动 kimi-webbridge start(幂等) |
| WEBBRIDGE_MCP_DEFAULT_SESSION | webbridge-mcp | 缺省会话名 |
| WEBBRIDGE_MCP_MOCK | 0 | 1 = 进程内假 daemon(有状态假页面),不碰真 daemon/浏览器(测试用) |
| WEBBRIDGE_MCP_DEBUG | 0 | 1 = 每步诊断(含 act 拒绝原因)打到 stderr |
行为与协议
- daemon 合约(v2.x):POST /command body {action, args, session};成功 {ok:true, data:{...}}(兼容 {ok:true, ...result}),失败 HTTP 502 + {ok:false, error:{code,message}}。
- 超时语义:所有调用都带客户端预算;超时返回 timedOut:true(变更类调用另带 unknownEffects:true), 绝不自动重试——daemon 可能在你放弃之后才落盘(mayHaveLateEffects),盲目重试就是重复写入。
- 错误传播:daemon 的 code/message 原样透出为 MCP isError 结果;obs/act 的拒绝是正常结果 (ok:false + stale:true + no_input_dispatched:true),不是 isError,便于脚本断言。
- 结果:统一以 text 块返回 JSON 字符串;screenshot/save_as_pdf 返回本地文件路径。
故障排查
- {"error":"... no extension connected"} → 浏览器扩展未连接:打开浏览器连接 Kimi WebBridge 扩展,再重试。
- session "x" has no tab — navigate or find_tab first → cdp 类调用要求会话先有标签: open_tab 或 find_tab。
- obs/act 返回 session_unbound → 上一次标签绑定失败/工作标签被关:再调一次 open_tab (会复用或重开),或在浏览器里自己打开目标页后调 open_tab(会被借用)。
- open_tab 返回 orphan_warning:true → 浏览器建了标签但扩展没能绑定:那个标签可能残留在浏览器里,手动关掉即可;重试前先看清提示里的 URL。
- navigate 超时 → 见上文机制;改用 open_tab,或在已借用的标签上用不带 newTab 的 navigate。
- 提示 "Please update the Kimi WebBridge extension" → 扩展版本落后,让用户更新扩展(不要自行处理)。
- Vivaldi(非官方支持浏览器)上 navigate(newTab:true) 必现 page load timeout (30s),且当前版本会无界挂死。extras/open-tab.mjs 是历史遗留的独立绕行脚本(本 wrapper 内置能力已覆盖,保留给不使用 MCP 的场景):
node extras/open-tab.mjs "https://example.com" my-session
node extras/open-tab.mjs "https://example.com" my-session --anchor "https://weibo.com" --daemon "http://127.0.0.1:10087" # Win 经隧道
安全注意
工具操作的是用户真实浏览器及其登录态。
- 不要在用户未要求时打开敏感页面;close_session 只在用户明确要求关闭标签时调用。
- 借用来的用户标签不是你的:只读使用,绝不导航/关闭。open_tab 不会把借用标签当作换页目标;但如果你自己用 find_tab(active:true) 借了用户的标签,随后 close_tab/close_session 会关掉它。
- 测试脚本的纪律:test/e2e-live.mjs 只用 URL 限定的清场(API + AppleScript 两次都以 fixture origin 为准),不会碰用户标签;历史异常残留可用 node test/cleanup-tabs.mjs --sessions <名,名> 按 URL 精确清理。
出处
页内原子快照(lib/snapshot.page.js)改编自 browser-use/jev-ultrafast 的 jev_ultrafast/snapshot.js(MIT);本仓库的三处偏离与理由写在 lib/snapshot.page.js 头部:命名空间改为 __wbObs、pageKey 去掉滚动与表单状态(几何与滚动都在 act 时重解析)、 guard 由数组改为具名字段对象(为了给出「哪个字段变了」的可执行拒绝原因)。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
LosEcher/kimi-webbridge-mcp
把本地 Kimi WebBridge 封装成 MCP 工具,让任意客户端操控真实浏览器
相关插件 · 浏览器
智能体插件框架deepseek-ai/deepseek-harness
DeepSeek Harnessdsh是由 DeepSeek AI 开发的开源 agent harness智能体框架。
★ 270,422已汉化DSH
71.9
多设备 Agent 看板YaoApp/yao
✨ 所有 Agent 和工作区集中管理,覆盖你的每一台设备。任务看板追踪进度,桌面端、移动端、浏览器、API…
★ 8,147已汉化DSH
71.2
可扩展侧边栏工作台omdsh-dev/DSH-better-sidebar
DSH 的双工作台:原生右侧栏 + 自绘底部面板,并把 ctx.betterSidebar 服务开放给所有插件
★ 4,761已汉化DSH
71.1
浏览器自动化桥Tencent/BrowserSkill
你的 Agent。你的浏览器。连接,就能协作。
★ 9,928已汉化DSH
71
AI 小说创作工作台EthanYoQ/AI-Novel-Writer
AI 小说作家 / AI Novel Writer
★ 1,591已汉化DSH
70.7
智能体浏览器引擎platonai/Browser4
DeepSeek Harness 集成
★ 1,175已汉化DSH
70.4
这是你的仓库?认领条目