内容
# Kitewright
**适用于 AI 代理的浏览器自动化,以单个小型二进制文件提供。** 一个 MCP 服务器(可流式传输的 HTTP + stdio),为 LLM 客户端提供 `navigate` / `screenshot` / `extract` 功能 — 无需携带 Node.js + Playwright 堆栈。

一行命令安装:
```bash
claude mcp add kitewright -- npx -y @kitewright/mcp
```
## 为什么 — 实测,而非声称
与 `@playwright/mcp` 0.0.78 版本进行对比,同一台机器,同一 Chromium headless-shell 版本,同一网页([完整方法论](BENCHMARKS.md)):
| | @playwright/mcp | **Kitewright** |
|---|---:|---:|
| 冷启动 → 监听中 | 354 毫秒 | **75 毫秒** |
| 服务器 RSS(空闲) | 102–125 MB | **7.6 MB** |
| 服务器 RSS(工作后) | 93 MB | **10.9 MB** |
| 分发 | 18 MB 软件包 + Node.js 运行时 | **6.9 MB 静态二进制文件** |
| 首次导航(包括浏览器启动) | 2623 毫秒 | **822 毫秒**(会话预热) |
| 预热导航延迟 | 80–116 毫秒 | 99–105 毫秒(不分胜负) |
| 空闲行为 | 浏览器保持活动 | **浏览器在空闲 TTL 后被回收**,在下一个会话中预热重启 |
浏览器本身(Chromium)的成本在任何语言中都相同 — 预热延迟不分胜负,因为两者都使用 CDP 与同一个浏览器通信。优势在于启动、分布、空闲占用和生命周期管理。差距在于:playwright-mcp 提供约 25 个工具,而我们提供 21 个工具 — 我们的路线图是缩小这一差距。
## 可靠性 — 动作自动等待
`click` / `type` / `fill_form` / `select_option` / `hover` 不会盲目触发。在执行操作之前,引擎会轮询(100 毫秒,最多 5 秒每次操作)直到目标元素 **存在**、**可见**(非 `display:none` / `visibility:hidden` / 零尺寸)、**启用**(无 `disabled` / `aria-disabled`)、**未被遮挡** 在其点击点,以及 **几何稳定** 跨两个连续帧。已解决的元素在第一次轮询时通过 — 但当操作无法进行时,您会收到一个 **特定原因** 的错误(`not found` / `not visible` / `disabled` / `covered` / `unstable`)而不是静默点击错误或通用超时。暂时的 CDP 错误会重试两次。传递 `timeout_ms` 以覆盖每个操作的 5 秒预算(例如,如果您希望元素已经存在,则使用较短的超时)。
## 性能
外部站点延迟是 **网络绑定** 的 — DNS + TLS + TTFB(~400–600 毫秒)是一个工具无法超越的底线,Kitewright 也不例外。但它 **攻击** 了网络周围所有可控的成本:
- **预热 + 预热上下文池。** 当 MCP 会话初始化时,服务器启动浏览器并在后台填充一个小型预创建的空白浏览器上下文池(`MCP_CONTEXT_POOL`,默认为 2)。然后为新会话提供一个准备好的上下文 + 页面,因此其 **首次** 导航支付零浏览器启动 * 和 * 零上下文创建成本。测量 localhost 首次导航:**~31 毫秒预热** vs ~709 毫秒冷启动(Apple Silicon;[详细信息](BENCHMARKS.md))。当浏览器空闲时,池会随着浏览器回收而排空,并在下一个需求时懒惰地补充,因此空闲占用保持在约 8 MB 基线。
- **精简模式。** `browser_navigate {lite:true}` (以及 `extract` / `extract_markdown` 的默认值)阻止图像/媒体/字体 + 广告/分析主机在加载前 — 30–70% 更快的 DOM-ready 加载重页面,通过跳过大部分字节。对于重型页面,截图/PDF 永远不会阻止资源。
- **共享磁盘缓存 + 连接预热。** 稳定的 `--disk-cache-dir` (`KITE_CACHE_DIR`)允许重复资产获取命中缓存跨运行; `KITE_PREWARM_URL` 在预热期间建立 DNS+TLS 到已知源。
诚实的框架:胜利是浏览器启动、页面权重、会话启动和连接设置 — 而不是到远程源的网络往返。
## 架构
```
crates/
├── engine/ kitewright-engine — CDP 核心(chromiumoxide):延迟启动,空闲回收,
│ 会话浏览器上下文,文本提取上限 + AX 快照。
│ 所有前端共享。
└── server/ kitewright — rmcp 流式 HTTP 服务器,通过 MCP 工具公开引擎。
bindings/
└── node/ @kitewright/node — napi-rs 绑定,提供与 Puppeteer 兼容的
(实验性)API,适用于同一个引擎(单独构建;不属于核心 cargo 工作区)。参见下面的 "@kitewright/node"。
```
每个 MCP 会话拥有其自己的 Chromium 浏览器上下文(代理之间的 cookie 隔离)中的一个持久页面:登录一次,继续点击。页面和上下文在会话结束时关闭;浏览器本身在空闲 TTL 后仍然被回收,并在下一次调用时透明地重启。
## 安装
零安装方式 — 直接从 **npx** 运行,如 `@playwright/mcp`:
```bash
claude mcp add kitewright -- npx -y @kitewright/mcp
```
`npx @kitewright/mcp` 为您的平台解析预构建的 `kite` 二进制文件(可选的每个平台依赖项,类似 esbuild),并启动一个 stdio MCP 服务器 — 无需 Rust 工具链,无需构建。参见 [`npm/kitewright-mcp`](npm/kitewright-mcp)。
更喜欢原始的 `kite` 二进制文件?(上面的 npx 是简单路径;这些需要 Rust 工具链。)
```bash
# 从公共仓库 — 构建并安装 `kite` 二进制文件:
cargo install --git https://github.com/kitewright/kitewright kitewright # → ~/.cargo/bin/kite
# 从本地checkout:
cargo install --path crates/server
# Docker(headless Chromium 捆绑在镜像中):
docker run --rm -p 8090:8090 kitewright # 本地构建:docker build -t kitewright .
```
> 尚未发布到 crates.io 或 Homebrew — `cargo binstall`/`brew` 未连接。使用 `npx @kitewright/mcp`(无工具链)或上面的 `cargo install --git` 命令。
### 获取浏览器
Kitewright 通过 CDP 驱动 Chromium,但不嵌入 Chromium。它使用系统 Chrome/Chromium,当存在时,会遵循 `BROWSER_EXECUTABLE`,如果两者都不存在,则会回落到由 `kite install` 下载的浏览器:
```bash
kite install # 将最新的稳定 chrome-headless-shell 获取到 kite 缓存中
```
`kite install` 将适用于您平台的当前 Chrome-for-Testing `chrome-headless-shell` 构建下载到 `$KITE_CACHE_DIR`(或 OS 缓存目录),引擎会自动拾取它 — 无需 `BROWSER_EXECUTABLE`。重新运行是一个 noop 操作,一旦存在构建。Docker 镜像已经包含 Chromium。
## 运行和连接
`kite` 无参数服务 MCP 以实现 **流式 HTTP** (联网,默认,支持身份验证和许多会话);`kite --stdio` 为本地客户端服务单个会话,使用 **stdio**:
```bash
kite
# → kitewright 监听 http://0.0.0.0:8090/mcp
```
**HTTP 传输** — 从 Claude Code 连接:
```bash
claude mcp add kite --transport http http://localhost:8090/mcp
```
**stdio 传输** — MCP 客户端配置(Claude Desktop,Cursor,…):
```json
{
"mcpServers": {
"kite": {
"command": "kite",
"args": ["--stdio"]
}
}
}
```
## 配置
| 环境变量 | 默认值 | 含义 |
|---|---|---|
| `MCP_HTTP_BIND` | `0.0.0.0:8090` | 监听地址 |
| `MCP_AUTH_TOKEN` | 未设置 | 设置后,`/mcp` 需要 `Authorization: Bearer <token>` (否则 401)。未设置 = 开放访问 + 启动警告 |
| `MCP_RATE_LIMIT_PER_MINUTE` | `300` | 每个客户端 IP 的请求限制(固定 60 秒窗口);超出时返回 429 |
| `BROWSER_EXECUTABLE` | 自动检测 | chrome / chromium / chrome-headless-shell 的路径。未设置时:检测系统 Chrome/Chromium,否则使用 `kite install`-管理的缓存目录中的构建 |
| `BROWSER_NO_SANDBOX` | 未设置 | 设置(任意值)以传递 `--no-sandbox` (容器) |
| `KITE_HEADLESS` | 未设置 | Kite 默认启动 **可见** (可见)浏览器,以便您查看自动化。设置(任意值)以运行 **无头** — 服务器、CI 和无显示的容器需要,带显示的 Chrome 失败启动 |
| `KITE_IDLE_TIMEOUT_SECS` | `1800` | 空闲秒数,之后无头浏览器被回收以释放内存。默认 30 分钟保持会话活跃(有头永远不会回收)。回收后通过 **自动恢复 cookie** 恢复,因此身份验证会话在回收后存活。降低内存受限的多会话服务器上的此值 |
| `KITE_ALLOW_SECRET_FILES` | 未设置 | 设置(任意值)允许 `browser_fill_secret` 从磁盘读取 `file:/path` 机密。**需要** `KITE_SECRET_DIR` (读取被限制在此目录内),以便不能读取/流出任意主机文件。环境变量机密不需要许可 |
| `KITE_VIEWPORT` | `1440x900` | 默认视口/窗口大小为 `WIDTHxHEIGHT` (Chromium 自身默认为紧凑的 800x600)。在运行时使用 `browser_resize` 工具进行调整 |
| `MCP_CONTEXT_POOL` | `2` | 预热的空白浏览器上下文数量,以便 **新** 会话获得即时可用的上下文+页面(零上下文创建延迟)。`0` 禁用。池在后台填充,并在浏览器空闲回收时排空(它永远不会让进程保持活动) |
| `KITE_CACHE_DIR` | `<tmp>/kitewright-cache` | 共享磁盘 HTTP 缓存( `--disk-cache-dir`),在启动之间稳定,以便重复资产获取命中缓存。注意:每个会话隔离上下文(cookie 隔离)使用临时缓存;这对浏览器的默认上下文有益 |
| `KITE_PREWARM_URL` | 未设置 | 如果设置,预热导航到此源以在第一个真正的导航之前建立 DNS+TLS+连接。未设置时不执行 |
| `BROWSER_PREWARM` | 未设置 | 设置(任意值)在服务器启动时启动 + 预热浏览器(否则在 MCP 会话初始化时预热) |
| `RUST_LOG` | `info` | 日志过滤器 |
## 安全
Kitewright 驱动一个真正的浏览器,因此将端点视为特权。默认设置是安全的本地使用;在公开之前请加强。
- **默认绑定回环(`127.0.0.1:8090`)。** 除非您显式设置 `MCP_HTTP_BIND`,否则它仅可从网络访问。如果您 **公开它,请设置 `MCP_AUTH_TOKEN`** — 没有它,`/mcp` 端点是未经身份验证的(启动时记录为警告)。身份验证使用恒定时间比较;请求受到速率限制;跨源浏览器请求被拒绝(DNS 重新绑定保护)。
- **SSRF 是浏览器工具固有的。** 调用者可以导航到内部或云元数据地址(`169.254.169.254`,RFC-1918,`localhost`) — 这是 **本地/开发自动化的重点**,但在公开实例时存在风险。Kitewright 不会阻止这些(默认情况下这样做会破坏本地自动化)。在公开部署时,请将其置于身份验证和网络策略之后,以无法访问敏感的内部端点。
- **默认情况下,禁止访问本地文件。** `file://` 导航需要 `KITE_ALLOW_FILE_URLS=1`;`browser_fill_secret` 文件读取需要 `KITE_ALLOW_SECRET_FILES=1` **和** `KITE_SECRET_DIR` 围栏(读取被规范化并必须在其下方)。 `env:` 机密不需要许可 |
## 工具(v0.4)
所有工具都在会话的持久页面上操作。
**读取**
- `browser_navigate {url, lite?}` — 标题,最终 URL,可见文本(上限)。`lite:true` 启用 **精简模式**:阻止图像/媒体/字体 + 广告/分析主机(doubleclick,google-analytics,GTM,facebook pixel,…)通过 CDP `Network.setBlockedURLs` 实现更快的 DOM-ready 加载重页面(重站点 30–70% — 文本,仅 **不** 在截图前使用)。 会话中默认为 lite;`extract` / `extract_markdown` 默认导航时为 lite;`screenshot` / `pdf` 永远不会阻止资源
- `browser_extract {url?, selector, attribute?}` — 从匹配选择器的元素中提取文本/属性
- `browser_extract_markdown {url?}` — 主要内容为 Markdown(“可读性”模式:标题/链接/列表/代码/表格,导航/脚本/样式剥离,限制约 20k 字符)
- `browser_screenshot {url?, full_page?}` — 当前页面的 PNG(`url` 导航后)
- `browser_pdf {url?, format?, landscape?, print_background?, display_header_footer?, header_template?, footer_template?, margin_top?, margin_bottom?, margin_left?, margin_right?, scale?, prefer_css_page_size?}` — 打印为 PDF(CDP `Page.printToPDF`);完整的 Puppeteer 选项集包括运行的页脚(合法文本,页码)和 CSS 单位边距(`"35px"`/`"20mm"`)。返回一个 JSON 信封 `{format, bytes, base64}`(MCP 没有原生 PDF 类型 — 解码 `base64` 获取文件)
- `browser_set_content {html, wait_until?}` — 将原始 HTML 字符串加载到当前页面(Puppeteer `page.setContent);`wait_until` 是 `load`(默认)/ `domcontentloaded` / `networkidle0`。与 `browser_pdf` 结合使用实现 HTML→PDF 渲染,无需服务器往返。处理大文档
- `browser_snapshot {diff?}` — 可访问性树快照(角色,名称,状态)限制约 15k 字符;`diff:true` 返回自会话中的上一次快照的更改(第一次调用是基线)
**调试**
- `browser_console {clear?}` — 页面上的控制台消息(log/warn/error/info)捕获;`clear:true` 清空缓冲区
- `browser_network {clear?, filter?}` — 页面上的网络请求(method, url, status, resourceType)捕获;`filter` 子字符串匹配 URL
**交互**
- `browser_click {selector, timeout_ms?}` — 滚动到视图 + 点击第一个匹配项
- `browser_type {selector, text, clear?, press_enter?, timeout_ms?}` — 关注并输入元素
- `browser_fill_form {fields: [{selector, value], timeout_ms?}` — 填充多个输入(每个字段 ok/错误摘要)
- `browser_fill_secret {selector, secret_ref, press_enter?, timeout_ms?}` — 输入一个机密(密码),其明文 **永远不会进入工具调用`:`secret_ref` 是 `env:NAME`(服务器环境变量)或`file:/path`(需 `KITE_ALLOW_SECRET_FILES` + 围栏 `KITE_SECRET_DIR`)。在服务器端解析,然后输入
- `browser_select_option {selector, value?, label?, timeout_ms?}` — 选择 `<option>` 按值或可见标签(触发 `change`)
- `browser_hover {selector, timeout_ms?}` — 将鼠标移动到元素的中心(显示 CSS `:hover` 菜单)
- `browser_press_key {key}` — 将 Enter / Tab / Escape / ArrowDown / ... 发送到聚焦的元素
- `browser_navigate_back {}` — 历史记录后退,返回新的标题 + URL
- `browser_handle_dialog {accept, prompt_text?}` — 预先设置自动接受/拒绝下一个 JS 对话框(s)
- `browser_wait_for {selector?, text?, timeout_ms?}` — 轮询直到选择器匹配或文本出现(默认为 10 秒,最多 30 秒)
**断言**
- `browser_assert {condition_selector?, condition_text?, should_exist?, timeout_ms?}` — 结构化 `{passed, checked, found, elapsed_ms}`(永远不会在条件失败时出错;`should_exist:false` 断言不存在)
### 选择器语法
`click` / `type` / `fill_form` / `select_option` / `hover` / `wait_for` / `extract` / `assert` 接受三种选择器形式(与内部元素句柄 API 相同 — `query` / `query_all` 返回 `ElementRef` 句柄,具有 `.click()` / `.type_str()` / `.text()` / `.attribute()` / `.bounding_box()` — 将支持 Wave-3 Puppeteer 样式的 npm 外观):
- **CSS** (默认)— 例如 `#login`,`button.primary`,`input[name="email"]`
- `text=<可见文本>` — 包含它的第一个可见元素
- `role=<角色>[name="<可访问名称>"]` — 匹配 ARIA 角色 + 可访问名称的元素,例如 `role=button[name="Submit"]`
角色解析是一种实用的 JS 启发式方法(隐含角色元素映射 + 可访问名称来自 `aria-label` / `aria-labelledby` / 关联 `<label>` / 文本 / `value` / `placeholder` / `title` / `alt`),不是完整的 ARIA 计算名称实现 — 它涵盖了代理通常定位的常见交互式角色。纯 CSS 提取仍然返回所有匹配项;`text=`/`role=` 提取返回单个解析元素。
## @kitewright/node(兼容 Puppeteer,实验性)
`bindings/node` 是一个 [napi-rs](https://napi.rs) 原生插件,为 `kitewright-engine` 提供了类似 Puppeteer 的外观 — 浏览器生命周期,CDP 和等待启发式在共享的 Rust 核心中原生运行,因此 JS 层很薄。它针对常见的 **HTML→PDF** 流程(例如发票/报告服务使用 Puppeteer 仅用于渲染)。对于该流程,迁移是一个一行导入更改:
```js
// import puppeteer from 'puppeteer'
import puppeteer from '@kitewright/node'
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] })
const context = await browser.createBrowserContext() // 每个发票的隔离
const page = await context.newPage()
await page.setContent(html, { waitUntil: 'networkidle0', timeout: 0 })
await page.evaluate(() => document.fonts.ready) // 返回 promise
const pdf = await page.pdf({ // → Node Buffer
format: 'a4', printBackground: true, displayHeaderFooter: true,
footerTemplate: legalFooterHtml, // 合法文本 + 页码
margin: { top: '20px', bottom: '35px' },
})
await page.close(); await context.close(); await browser.close()
```
每个 `Page` 都是一个持久的引擎会话,位于其 **自己的** Chromium 浏览器上下文中,这正是 Puppeteer 承诺的每个 `createBrowserContext()` 隔离。`page.setContent` + `page.pdf(footerTemplate)` 生成一个有效的,多页的 PDF 文件,带有运行的页脚(在 `bindings/node/test/invoice.e2e.mjs` 中验证,镜像发票服务的真实流程,并写入 `test/out/invoice.pdf`)。
### 兼容性矩阵
| 支持 | 不支持(抛出明确错误) |
| --- | --- |
| `puppeteer.launch({ headless, args, executablePath })` | 请求拦截(`setRequestInterception`) |
| `browser.newPage()` / `browser.createBrowserContext()` / `browser.close()` | 跟踪(`page.tracing`) |
| `context.newPage()` / `context.close()` | 设备/视口模拟(`setViewport`,`emulate`) |
| `page.setContent(html, { waitUntil })`(`load` / `domcontentloaded` / `networkidle0`) | `page.screenshot`(使用MCP的`browser_screenshot`工具) |
| `page.evaluate(fn \| string)` 包括返回Promise的函数体 | `page.waitForSelector`,`addScriptTag`,`exposeFunction` |
| `page.pdf(options)` — 完整选项集(`format`,`landscape`,`printBackground`,`displayHeaderFooter`,`header/footerTemplate`,`margin`,`scale`,`preferCssPageSize`)→ Buffer | `puppeteer.connect`(远程浏览器) |
| `page.goto(url)` / `page.close()` | `--no-sandbox` 被遵守;其他Chromium `args` 被忽略;`headless:false` 被忽略(始终为headless) |
注意:`waitUntil: 'networkidle0'` 近似为加载 + 短暂稳定(内联 `setContent` 内容,无拦截)。不支持的方法会抛出错误,而不是静默无效,以便调用者立即发现问题。
### 构建(本地,此平台)
插件单独于核心Cargo工作区构建(它被列在 `[workspace] exclude` 下,因此 `cargo clippy --workspace` / Rust测试从未触及napi工具链):
```bash
cd bindings/node
npm install
npx napi build --release --js binding.js --dts binding.d.ts # 生成 kitewright-node.node
BROWSER_EXECUTABLE=/path/to/chrome node --test test/invoice.e2e.mjs
```
## kite-pdf — HTML/Typst → PDF
`kite-pdf` 是一个专注的 **文档 → PDF** 渲染服务和CLI,构建在相同的引擎(crate `crates/pdf`,二进制 `kite-pdf`)上。它有两个后端,通过Cargo功能在构建时选择,在运行时按请求选择:
- **Chromium** — `html`/`url` → PDF 通过共享的 `kitewright-engine`(无头Chromium,完整的 `Page.printToPDF` 选项集:页眉/页脚模板,边距,横向,背景,缩放,CSS页面大小)。
- **Typst** — 一个 [Typst](https://typst.app) `template` + JSON `data` → PDF **无需浏览器**。编译器和字体嵌入在二进制文件中;渲染是纯CPU,语言无关,且可复制。
### 一个crate,三个构建形态(相同的二进制名)
| 构建 | 功能 | 后端 | 大小 | 适用于 |
| --- | --- | --- | --- | --- |
| `kite-pdf`(默认) | `chromium` + `typst` | HTML **和** Typst | ~43 MB(macOS arm64,release+LTO) | 您需要两者;一个二进制文件可以渲染任何内容。 |
| `kite-pdf-chromium` | `--no-default-features --features chromium` | 仅HTML | 最小二进制文件(无Typst/字体)+运行时浏览器 | 您只渲染HTML/URL;跳过Typst编译器+捆绑字体。 |
| `kite-pdf-lite` | `--no-default-features --features typst` | 仅Typst | ~39 MB,**无浏览器** | 您控制模板;想要一个无浏览器,无依赖的服务。 |
```bash
cargo build --release -p kite-pdf # 两个后端
cargo build --release -p kite-pdf --no-default-features --features chromium
cargo build --release -p kite-pdf --no-default-features --features typst
```
### HTTP API
`POST /render` 请求带有JSON主体;响应带有 `application/pdf` 字节(200)或JSON `{ "error": "..." }`(400客户端/500服务端)。`GET /healthz` 返回编译后的后端。绑定地址:`KITE_PDF_BIND`(默认 `0.0.0.0:8091`)。
```jsonc
{
"engine": "chromium" | "typst", // 可选;否则推断(html/url→chromium,template→typst)
"html": "<!doctype html>...", // chromium
"url": "https://...", // chromium
"template": "= Invoice ...", // typst 源代码
"data": { "number": "INV-1" }, // JSON,暴露给模板作为 sys.inputs.data
"format": "A4" | "Letter" | "Legal" | "A3",
"landscape": false,
"print_background": false,
"display_header_footer": false,
"header_template": "<div>...</div>",
"footer_template": "<div>... <span class=\"pageNumber\"></span> ...</div>",
"margin": { "top": "20px", "bottom": "40px", "left": "15px", "right": "15px" }
}
```
请求未编译到运行二进制文件的后端会返回明确的 **400** (例如 `"typst backend not compiled in this build — use the full or -lite build"`)。在Typst模板中,使用以下方式读取注入的数据:
```typst
#let data = json(bytes(sys.inputs.data))
= Invoice #data.number
```
```bash
# Chromium: 渲染HTML字符串
curl -sX POST localhost:8091/render \
-H 'content-type: application/json' \
-d '{"html":"<h1>Hello</h1>"}' -o hello.pdf
# Typst: 数据驱动发票,无浏览器参与
curl -sX POST localhost:8091/render \
-H 'content-type: application/json' \
-d '{"template":"#let d=json(bytes(sys.inputs.data))\n= Invoice #d.number","data":{"number":"INV-7"}}' \
-o invoice.pdf
```
> **注意:** 渲染服务默认**无认证** — 在受信任的网络中运行或在网关后面运行。(带有速率限制的Bearer认证,镜像 `kite` 服务器的 `HttpGuard`,是一个TODO。)
### CLI
```bash
# Chromium: HTML文件 → PDF,带有页脚模板 + 边距
kite-pdf render --html-file invoice.html --footer-file footer.html \
--margin-top 20px --margin-bottom 40px -o invoice.pdf
# Typst: 模板 + 数据 → PDF(无浏览器)
kite-pdf render --template invoice.typ --data invoice.json -o invoice.pdf
# 运行HTTP服务(也是默认的不带参数)
kite-pdf serve
```
### Docker
```bash
# 完整服务(slim Debian + Chromium;两个后端)。从存储库根目录构建:
docker build -f crates/pdf/Dockerfile -t kite-pdf .
# 无浏览器,distroless,仅Typst:
docker build -f crates/pdf/Dockerfile.lite -t kite-pdf-lite .
docker run -p 8091:8091 kite-pdf
```
### 公平比较
- **vs [Gotenberg](https://gotenberg.dev):** kite-pdf是最轻量的自托管HTML→PDF选项 — 一个小型二进制文件,惰性浏览器生命周期,当空闲时终止。Gotenberg在您需要**办公文档转换**(DOCX/XLSX/ODT通过LibreOffice)和包含电池的API时获胜;kite-pdf**不**执行办公格式。
- **vs [react-pdf](https://react-pdf.org) / 客户端PDF库:** Typst路径是**无浏览器和语言无关**的 — 无Node运行时,无React,无无头Chrome — 只需来自任何语言的模板+JSON通过HTTP。您放弃React的组件模型,以换取更小,更快,可复制的排版管道。
- **它让步的地方:** 没有办公文档(DOCX/XLSX)转换,并且Chromium后端仍然需要在运行时使用浏览器(Typst/`-lite`后端则不)。
## 路线图
- [x] `snapshot` — 可访问性树快照(token-budgeted)
- [x] `click` / `type` / `press_key`
- [x] `wait_for` (选择器/文本轮询)
- [x] `fill_form` / `select_option` / `hover` / `navigate_back` / `handle_dialog`
- [x] 存储状态(`save_state` / `restore_state`)— 跨会话重用登录
- [x] 角色/文本选择器(`text=`,`role=…[name="…"]`)与CSS并列
- [x] `assert` — 结构化的通过/失败原语,用于代理驱动的功能测试
- [x] Markdown(可读性)提取模式
- [x] `pdf` — 将当前页面打印到PDF(`Page.printToPDF`)
- [x] `kite-pdf` — 独立的HTML/Typst → PDF 服务 + CLI(双后端,三个功能门控构建形状)
- [x] 行为自动等待(可见/启用/无阻塞/稳定)带有原因特定的错误
- [x] `console` / `network` 捕获用于调试
- [x] `snapshot {diff}` — 仅自上次快照以来发生的变化
- [x] 元素句柄原语(`query` / `query_all` → `ElementRef`)— npm外观的基础
- [x] 每个MCP会话的浏览器上下文(cookie隔离)而不是每个调用的页面
- [x] Bearer-token认证 + 速率限制
- [x] `bindings/node`: napi-rs Puppeteer兼容外观(`@kitewright/node`,实验性)— launch/newPage/createBrowserContext/setContent/evaluate/pdf/goto/close;HTML→PDF流程已端到端验证
- [ ] `bindings/node`: 预构建的每个平台二进制文件 + npm发布
- [ ] Python绑定(PyO3)
- [ ] README的基准测试与playwright-mcp(冷启动,RSS,图像大小)
- [ ] 预构建的发布二进制文件(macOS/Linux/Windows)+ `cargo binstall` + Homebrew tap
## 非目标
Kitewright故意是一个精简的**代理工具**,而不是QA测试框架。它不会:
- **支持多个浏览器引擎。** CDP / Chromium 仅,由设计 — 没有Firefox或WebKit。向一个引擎讲一个协议保持二进制文件小且生命周期简单。
- **提供测试运行器,fixtures,跟踪查看器或视频捕获。** 这些属于QA框架(Playwright Test,Cypress)。Kitewright提供代理原语(`snapshot`,`assert`,存储状态);代理或薄脚本是运行器。
- **公开任意JS `eval`工具。** 执行代理或模型编写的JavaScript代码对抗实时会话(带有恢复的cookie)是一个安全隐患。选择器解析和帮助程序运行精选的固定JS代码。
## 许可证
MIT
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器