mcpField
K
kite-lite

kite-lite

本地服务
github.com浏览器自动化011 周前更新

关于 kite-lite

kite-lite 是面向 Agent 的轻量网页引擎和 MCP 服务,可在不使用真实浏览器的情况下获取、检查、交互和渲染网页。它解析 HTML,但不执行页面 JavaScript,适合静态内容与结构化审计,不适合依赖 JavaScript 的网站。

主要功能

  1. 01HTML 解析与页面文本提取
  2. 02支持 Cookie 和重定向的有状态导航
  3. 03不执行页面 JavaScript 的表单交互
  4. 04隔离的 JavaScript 求值
  5. 05SVG、PNG 和 PDF 渲染
  6. 06结构化网页、无障碍、SEO 与社交审计

适用场景

  1. 01提取 HTML 页面的文本与链接
  2. 02浏览不依赖客户端 JavaScript 的多步骤流程
  3. 03通过截图检查基础页面布局
  4. 04运行确定性的无障碍与 SEO 检查

MCP 相关

kite-lite 可通过 stdio 作为 MCP 服务运行。其工具允许 MCP 客户端获取和检查网页、导航会话、操作简单表单、运行隔离的 JavaScript 并渲染截图。

配置

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

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库
安装包
cargokite-lite0.1.3

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

kite-lite

M8ven Verified

mcp-name: io.github.MauricioPerera/kite-lite

面向 Agent 的轻量级、自包含 Web 引擎。它旨在运行于 Cloudflare 之外的 VPS 或容器中,具备 HTML 解析器、隔离的 JavaScript、确定性 SVG 渲染以及精简的 CDP 接口。

当前状态

Rust DOM 核心:

  • 使用兼容 HTML5 的解析器解析 HTML;
  • 提取标题、文本和 JSON 格式的链接,并将每个相对链接解析为相对于页面最终 URL 的绝对 URL;
  • 保留树形表示,以便添加 CSS、JavaScript 和渲染;
  • 在解析和本地求值期间避免使用全局状态;
  • 在 CDP 会话中保留最终 URL(重定向后)和源 HTML;
  • 通过自带 cookie jar 的 HTTP 客户端,在同一会话的多次导航之间(fetch/CDP)保留 Cookie;
  • 使用固定的默认样式表按标签计算每个元素的最简布局(Element.layout 中的 {x, y, width, height}),包括 h1-h6 的字号/粗体以及 p/li/blockquote 的边距,并支持文本换行(word wrap)——限制请参阅“最简布局”一节; 无需执行页面 JavaScript,即可通过 CDP 点击、输入和提交表单——请参阅“交互:点击、输入和提交表单”; 除 SVG 外,还可渲染为 PNG 和 PDF——请参阅“PNG/PDF 渲染”; 可作为 MCP 服务通过 stdio 运行——请参阅“MCP 服务”。

面向 Agent 的能力

kite-lite 为 Agent 提供 Web 访问能力,无需拖上一个真正的浏览器 (Chromium/Playwright)——测得的内存峰值约为 4-7 MiB(参见“最低资源 要求”),毫秒级启动,在 --network=none/ --cap-drop=ALL/--read-only 下运行也不会崩溃。这改变了成本计算方式: 无需共享一个笨重实例,而是可以按任务启动一个实例,并在小型 VPS 上并行 运行。

具体来说,它支持:

  • 低成本读取页面(fetch_page)——返回轻量摘要 (标题/文本/链接),而非包含布局的完整 DOM。当 Agent 只需要知道“这个 页面写了什么”时,比返回原始 HTML 更节省上下文。
  • 有状态会话(browser_navigate + 持久化的 Cookie/重定向)——支持 多步流程(登录 → 受保护的页面),前提是流程不依赖客户端 JavaScript。
  • 无需执行页面 JavaScript 即可交互表单——点击、输入、提交——相当于 禁用了 JavaScript 的浏览器(参见“交互:点击、输入和提交表单”),支持 按标签、type 或 name 选择。
  • 隔离且可验证的 JS(eval_js)——提取计算结果或测试表达式,并具备 真正的沙箱保证:无网络、无文件系统,运行在独立进程中,甚至无法链接 reqwest/tokio(参见“MCP 服务”)。
  • 低成本视觉呈现(render_screenshot/browser_screenshot)——用于 大致检查或报告,而非像素级视觉 QA(参见“最低限度布局”)。
  • 结构化审计,而非依赖 LLM 判断——四个 lint 工具 (webmcp-lint、a11y-lint、seo-lint、social-lint)为构建或审查 网站的 Agent 提供确定性且低成本的检查关卡,无需浪费一次模型调用来检查 是否缺少 <meta description>。
  • 声明式 WebMCP(browser_call_tool)——如果目标网站发布了带注解的 表单,Agent 可以使用带 schema 的接口,而不必猜测选择器(参见“声明式 WebMCP 支持”)。

这些是有意设置的硬性限制——并非 bug,而是上述能力的直接代价(原因请参见 “后续层”):

  • 无法处理依赖客户端 JS 的内容——SPA、通过客户端 fetch 加载的内容, 以及任何在浏览器中渲染的框架,都不可见。
  • 不支持真正的交互——不会运行 onclick 或任何实际事件,只支持导航 (<a>)和 GET 提交。
  • 表单仅支持 GET——无法发送真正的 method="post" 请求体。
  • 不支持多标签页、拖放、文件上传或无限滚动。
  • 并非 Playwright——WebSocket CDP 可以连接,但任何依赖对活动 DOM 调用 Runtime.callFunctionOn 的操作(Playwright 内部就是这样工作的)都无法 使用(参见“CDP 兼容性”)。

适用于:以纯 HTML 提供的内容(博客、文档、大多数内容网站、返回 HTML 的 API、简单的 GET 表单);大规模读取/提取/审计,而使用真正浏览器的成本不 合理;或者将隔离(JS 无网络、没有活动 DOM)视为安全优势而非限制的任何 场景。对于 SPA、复杂交互或通过客户端 JS 进行验证的网站,则需要真正的浏览 器——没有捷径。

试用

cargo test
cargo run -- https://example.com
cargo run -- https://example.com --svg example.svg --js "1 + 2"
cargo run -- https://example.com --png example.png
cargo run -- https://example.com --pdf example.pdf
cargo run -- fetch https://example.com --output page.json
cargo run -- eval page.json --js "document.title"
cargo run -- render page.json --output page.svg
cargo run -- render page.json --output page.png
cargo run -- render page.json --output page.pdf
cargo run -- serve 127.0.0.1:8787
cargo run -- cdp page.json 127.0.0.1:9222
cargo run -- cdp 127.0.0.1:9222

render(以及 POST /v1/render,通过 ?format=png|pdf|svg)会根据 --output 路径的扩展名选择输出格式(其他情况下默认为 .svg)。

--js 会在全新的 Boa JavaScript 上下文中运行脚本,不提供文件系统、网络或宿主绑定。页面快照会公开 document.title、document.body.innerText,以及仅支持查询第一个 h1、h2、h3、p、a 或 button 的有限版 document.querySelector()。--svg 会按照下文所述的最小布局(参见“最小布局”)生成确定性渲染:每个含文本的叶子元素都会根据每种标签的固定默认样式进行换行和定位。

示例:

cargo run -- https://example.com --js "document.title + ' / ' + document.querySelector('h1').innerText"

JavaScript 上下文现在设有源码大小、递归和 VM 栈限制。此外,CLI 会在独立的 kite-lite-js 进程中运行页面脚本,并在 1.5 秒后终止该进程。该求值器二进制文件完全没有链接 reqwest/tokio,因此无论脚本尝试做什么,都无法发出网络请求——这种隔离是可执行文件编译内容的特性,而不仅仅取决于代码选择调用什么。这是一个实用的本地隔离边界;生产部署仍应添加操作系统级资源限制,并使用容器或微型虚拟机。

使用系统限制运行

Docker 镜像以非特权用户身份运行该二进制文件。要使用严格的资源限制进行测试:

docker build -t kite-lite .
docker run --rm `
  --memory=256m --cpus=0.5 --pids-limit=64 `
  --read-only --tmpfs /tmp:rw,noexec,nosuid,size=16m `
  --cap-drop=ALL --security-opt=no-new-privileges `
  kite-lite https://example.com --js "document.title"

在此模式下,需要访问网络才能下载 URL。对于已序列化的页面,可以在无网络的情况下运行求值器——而且,由于求值器(kite-lite-js)是一个未链接 reqwest 或 tokio 的独立二进制文件,--network=none 是二进制文件本身的真实保证,而不仅仅是容器配置:

docker run --rm --network=none --memory=256m --cpus=0.5 --pids-limit=64 `
  --read-only --tmpfs /tmp:rw,noexec,nosuid,size=16m `
  --cap-drop=ALL --security-opt=no-new-privileges `
  -v "${PWD}/page.json:/input/page.json:ro" `
  kite-lite eval /input/page.json --js "document.title"

渲染也可以在无网络的情况下运行:

docker run --rm --network=none --read-only --cap-drop=ALL `
  -v "${PWD}/page.json:/input/page.json:ro" `
  -v "${PWD}:/output" `
  kite-lite render /input/page.json --output /output/page.svg

最小布局

fetch、CDP 中的 Page.navigate/Page.reload,以及 POST /v1/parse 会根据树计算简单的块级布局,并在返回 Page 之前将其保存到 Element.layout({x, y, width, height})中。render_svg 会根据要求渲染的精确宽度在内部重新计算布局(不会复用 JSON 中保存的布局,以免在要求不同宽度时出现偏移)。

这不是 CSS 引擎:

  • 不支持 <style>、style="..." 或类/ID——尺寸、粗体和边距按标签名称固定(h1-h6、p、li、blockquote、strong/b),是对默认用户样式表的最小模拟;
  • 不支持真正的行内布局:任何有子元素的元素都会将子元素按垂直方向全宽堆叠。这是当前 DOM 树的真实限制,而不仅仅是简化处理:解析器会将元素中散落的文本与其子元素的文本混合到一个 text 字段中(不会保留顺序,也不会将文本作为同级节点保存),因此,当父元素有子元素时,散落的文本就无法单独计算——例如 <p>Hola <a href="/x">link</a></p>;只有叶子节点自身的文本才会换行并绘制;
  • x 始终为 0.0:布局没有水平轴,只会垂直堆叠;
  • 不支持边距折叠、浮动或定位。

PNG/PDF 渲染

render_svg 可以栅格化为 PNG(resvg + tiny-skia,纯 Rust,无需依赖浏览器或外部工具),然后可以将该 PNG 封装为单页 PDF(printpdf)。

运行二进制文件的系统必须安装字体:render_svg 绘制的全部内容都是文本(没有矩形、图像或其他图形),而栅格化器使用 fontdb::load_system_fonts()——不包含任何嵌入字体。没有字体时,PNG/PDF 中的文本会缺失(其他元素也不存在,因此同样不会显示)。Docker 镜像会为此安装 fonts-dejavu-core;在 Docker 之外进行本地构建时,则取决于操作系统是否提供可用字体。

该 PDF 不是矢量格式:它是封装在 96 DPI PDF 页面中的栅格化 PNG 图像——其中没有可选择或搜索的文本。真正的矢量 PDF 需要拥有独立于栅格化的字体处理流水线(printpdf 原生支持 SVG,但内部使用了旧版本的 usvg,并以未加载字体的 Options::default() 运行,且没有注入字体的方法——因此不适用于文本)。

本地控制 API

服务器默认绑定到 loopback,并提供:

  • GET /health
  • POST /v1/parse,请求体中包含 HTML(链接会保持相对路径,因为此端点不接收用于解析链接的基础 URL);
  • POST /v1/render,请求体中包含 JSON 快照——?format=svg(默认)、png 或 pdf;
  • POST /v1/eval,请求体为 { "page": ..., "script": "..." }。

不要将其直接暴露到 Internet;如需远程访问,请将其置于身份验证和代理之后。

CDP experimental

cdp 模式会打开一个 WebSocket,兼容 Chrome DevTools Protocol 的一小部分:

  • Browser.getVersion;
  • Runtime.enable、Page.enable、Network.enable、DOM.enable;
  • Runtime.evaluate;
  • Page.getNavigationHistory、Page.getResourceTree;
  • Page.navigate、Page.reload、Page.captureSnapshot;
  • DOM.getDocument、DOM.querySelector、DOM.querySelectorAll;
  • DOM.getOuterHTML、DOM.getAttributes、DOM.getBoxModel;
  • Input.dispatchMouseEvent、Input.dispatchKeyEvent;
  • Target.getTargets、Target.getTargetInfo、Target.attachToTarget、 Target.attachToBrowserTarget、Target.setAutoAttach、 Target.setDiscoverTargets、Target.closeTarget、 Target.disposeBrowserContext——只有一个固定目标,不支持真正的多标签页 (参见下文“CDP 兼容性”);
  • 基本事件 Page.frameStartedLoading、Page.loadEventFired 和 Page.frameStoppedLoading(在 Page.navigate/reload 后,以及点击后导致导航时都会触发),还有 Target.attachedToTarget。

使用 JSON 快照运行 cdp,即可从序列化页面启动会话。未提供快照时,会启动一个空白页面:

cargo run -- cdp page.json 127.0.0.1:9222

Page.navigate 和 Page.reload 会使用每个 CDP 会话的持久 HTTP 客户端(在运行中的 cdp 进程生命周期内)下载 HTML、重建 DOM,并将 URL 更新为跟随重定向后的最终目标 URL,而不是最初请求的 URL。网站设置的 Cookie(包括重定向中间跳转时设置的 Cookie)会保存在该会话的 Cookie jar 中,并在同一 cdp 进程内的后续导航中重新发送。这正如需要在登录页和后续页面之间保持登录状态的 Agent 所期望的那样。Runtime.evaluate 只会公开文档的精简快照;不提供 fetch、文件系统或宿主绑定。

除了自动重新发送外,服务器设置的每个 Cookie(Set-Cookie 标头)也会在 Page.cookies 中公开(名称、值,以及 domain/path/secure/http_only/same_site 等标志,如果有的话)——此前 Cookie 只存在于 reqwest 的内部 Cookie jar 中,无法读取。MCP 的 fetch_page 和 browser_navigate 会在存在 Cookie 时包含此字段。限制:只能看到 kite-lite 发起的 fetch 请求中 HTTP 标头里的 Cookie;看不到页面 JS 设置的 Cookie,因为页面 JS 不会执行。

CDP 调用的概念示例:

{"id":1,"method":"Page.navigate","params":{"url":"https://example.com"}}
{"id":2,"method":"DOM.querySelector","params":{"nodeId":1,"selector":"h1"}}
{"id":3,"method":"DOM.getOuterHTML","params":{"nodeId":6}}

目前的实现尚不支持通过 CDP 捕获 PNG/PDF(请改用 render 或 /v1/render?format=png|pdf——参见“PNG/PDF 渲染”),也不支持执行页面脚本或网络事件。

CDP 兼容性

cdp 服务在同一端口上同时提供 WebSocket 和 Chrome 在其 remote-debugging 端口上提供的 HTTP 发现端点(GET /json/version、GET /json、GET /json/list)——通用工具(chrome-remote-interface、启动时会先请求 webSocketDebuggerUrl 的脚本等)也会查询这些端点,然后再连接 WebSocket。它还实现了 Target 域的最低限度功能(getTargets、attachToTarget、setAutoAttach、……),因此,期待真实 CDP 流程(操作前先附加到 target,且每个页面响应/事件都带有 sessionId)的客户端不会一直等待永远不会到来的内容——不过,这只是一个固定的模拟 target:没有多个真实标签页/target,attachToTarget 始终返回同一个 sessionId,而且所有 WebSocket 连接仍共享唯一的 Page,与上文所述一致。

这并不会启用真正的 Playwright。 playwright.chromium.connectOverCDP() 可能因此能够连接并附加,但 Playwright 的每项操作(page.click()、page.fill()、locator())都会执行注入的 JavaScript,通过 Runtime.callFunctionOn 对活动 DOM 进行检查,以便在操作前检查可见性/滚动/可操作性——这需要项目刻意避免的、精确的 DOM↔JS 关联并持久化(原因见“交互”和“最小布局”)。我们没有尝试模拟这些功能;否则会让人以为它能正常工作,直到第一次真正点击时才暴露问题。

交互:点击、输入和表单提交

Input.dispatchMouseEvent(type: "mousePressed")和 Input.dispatchKeyEvent(type: "char"/"keyDown")实现的是不执行页面 JS的交互——类似禁用了 JavaScript 的浏览器:

  • 点击布局框包含事件 y 坐标的节点:
    • <a href> → 导航到该 URL(与 Page.navigate 使用相同路径:会应用 Cookie、重定向和会话 URL 解析);
    • <input>/<textarea> → 聚焦该节点,以便 Input.dispatchKeyEvent 知道输入位置(任何导航都会丢失焦点);
    • <button> 或 <input type="submit"> → 查找最近的祖先 <form>,将其后代 <input>/<textarea> 当前的 name/值汇总成查询字符串,并导航到 action?query(如果没有 action,则导航到当前 URL)。
  • Input.dispatchKeyEvent 的 type:"char" 会将 text 附加到聚焦节点的 value;type:"keyDown" 且 key:"Backspace" 时,会删除最后一个字符。

点击不会执行任何 onclick,也不会运行页面的 <script>——由于与“最小布局”相同的原因,仍然没有与 JS 关联的活动 DOM。其他限制:点击仅依据坐标 y(布局中没有 x 轴——见“最小布局”);只会构造 GET 提交(不支持 method/action 为 POST 的情况,也没有请求正文);<select>/复选框/单选框没有各自的语义,会像其他节点一样处理,不执行特殊操作。要确定点击位置,真正的 CDP 客户端会先执行 DOM.querySelector,再执行 DOM.getBoxModel 获取坐标——这与 Playwright/Chrome DevTools 相同。

MCP 服务

cargo run -- mcp 通过 stdio 运行 kite-lite,作为 MCP(Model Context Protocol)服务器:stdin/stdout 中以换行符分隔的 JSON-RPC 2.0,与 Claude Desktop/Claude Code 用于启动本地工具的传输方式相同。手动使用 serde_json 实现(不使用 MCP SDK)——与项目其余部分的 CDP 实现理念相同:所需的接口(initialize、tools/list、tools/call)很小。

公开的工具,全部基于现有引擎:

  • fetch_page(url) — 轻量级摘要 {url, title, text, links},不包含带布局的完整 DOM 树(对 Agent 的上下文来说会产生过多 JSON)。不执行页面 JS,也不触及持久会话。
  • render_screenshot(url, format?) — 获取 URL 并将其渲染为 PNG(base64 图像)或 SVG(文本)。与“PNG/PDF 渲染”有相同限制:系统中没有字体时,输出会是空白。
  • eval_js(url, script) — 获取 URL,并在快照上对 JS 求值,使用与以往相同的沙箱(无法访问网络/文件系统/真实 DOM)。
  • browser_navigate(url)、browser_click(selector)、browser_type(text, selector?)、 browser_get_dom(selector?)、browser_screenshot(format?) — 每个进程只有一个持久导航会话(与 cdp 一样,不支持多标签页),cookies/重定向行为与项目其余部分相同。browser_click 复用“交互:点击、输入和表单提交”的相同逻辑,但通过 selector 而非 y 坐标定位元素——这对 MCP 工具来说更自然。selector 并非真正的 CSS:可以是 tag(精确名称,例如 button)、表示任意标签的 *,或按 type 或 name 筛选的 tag[attr=value]/[attr=value](例如 input[type=submit])——这是内部 DOM 作为独立字段保存的仅有两个属性。不支持类、id 或组合选择器。实践中这一点很重要:<input type=text> 和 <input type=submit> 的标签相同,因此不使用属性筛选时,只能匹配文档顺序中的第一个。

工具出错(selector 未匹配、fetch 失败等)时,会以 isError: true 返回,并将错误消息放在内容中——这样 Agent 就能看到错误并作出响应,而不是导致整个 JSON-RPC 调用失败。

此外,还使用真实的 MCP 客户端进行了验证,而不只是手动交互协议:scripts/mcp_ollama_bridge.py 将 Ollama Cloud 模型(支持原生工具调用)连接到 kite-lite 的 MCP 服务器,让模型自行决定调用哪个工具以及使用什么参数——借此测试了全部 9 个工具,包括完整流程 browser_navigate → browser_click → browser_type → 表单提交,以及两种格式的屏幕截图。它也已在 claude_desktop_config.json 中配置,可供 Claude Desktop 使用,但尚未通过应用中的真实对话验证这项具体集成。

WebMCP 声明式支持

除了自有工具外,kite-lite 还会检测带有 WebMCP 声明式属性(toolname、tooldescription、toolparamdescription、toolautosubmit、 required)的表单,并将其作为额外的 tools 暴露出来:

  • 任何页面摘要(fetch_page、browser_navigate、 browser_click)都包含一个 tools 字段,列出检测到的 tools:其 名称、描述、autosubmit,以及根据表单字段生成的 JSON inputSchema(<select> 会变成包含其 <option value> 的 字符串 enum;type="checkbox"/"number" 会分别推断为 boolean/number;其余则为 string)。
  • browser_call_tool(name, arguments?) 会用 arguments 填充这些字段(缺少参数时使用字段当前值),并提交表单,效果与对提交按钮调用 browser_click 相同。

明确限制:仅支持声明式子集(HTML 属性)——不支持命令式 API(JS 中的 navigator.modelContext.registerTool()),因为这需要针对活动 DOM 执行页面 JS,而这正是 kite-lite 刻意避免的(见下文“后续层”)。 表单只能通过 GET 提交,且不带请求正文——这与点击提交按钮时的限制相同。

WebMCP 声明式 Linter

kite-lite webmcp-lint <url|page.json|archivo.html> [--json] 会根据实用规则验证页面上的 toolname="..." 表单,以便在发布前进行检查:

cargo run -- webmcp-lint ./mi-pagina.html
cargo run -- webmcp-lint https://mi-sitio.com/checkout
cargo run -- webmcp-lint ./mi-pagina.html --json   # para consumir desde otro script

检查项按严重程度从高到低排列:

  • 错误(退出代码 ≠ 0,适用于 CI 阻断):缺少 tooldescription; 页面中有两个表单使用相同的 toolname(Agent 无法区分应调用哪一个)。
  • 警告:toolname 含有 [A-Za-z0-9_-] 以外的字符(某些 tool-calling 后端会拒绝);表单没有 action; 字段没有 name(不会纳入 schema,Agent 无法看到);<select> 中没有任何 <option>(enum 为空)。
  • 信息:表单为 method="post"——kite-lite 只能模拟 GET 提交,因此 browser_call_tool 无法反映真实行为;建议也在支持原生 WebMCP 的浏览器中测试。字段没有 toolparamdescription(非必填,但有助于 Agent)。

它不能替代在启用了 WebMCP 的真实浏览器中进行测试——这是抵达那一步之前,快速检查最常见错误的本地方式。

无障碍 Linter(a11y)

kite-lite a11y-lint <url|page.json|archivo.html> [--json]——输入和输出格式与 webmcp-lint 相同,但用于检查少量实用的无障碍规则,并非完整的 WCAG 审计:

cargo run -- a11y-lint ./mi-pagina.html
cargo run -- a11y-lint https://mi-sitio.com

规则目前全部为 Warning(即使发现问题,命令也会以代码 0 退出——严重程度划分已就绪,以便将来添加确实需要导致构建失败的规则):

  • <img> 缺少 alt 属性(装饰性图片使用 alt="" 不算缺失)。
  • <a> 没有文本,且没有带非空 alt 的后代图片。
  • 页面中有多个 <h1>。
  • 标题级别跳跃(<h1> 后直接跟 <h3>,中间没有 <h2>)。
  • <html> 缺少 lang 属性。
社交媒体预览(Open Graph / Twitter Card)

kite-lite social-lint <url|page.json|archivo.html> [--json] 会模拟 Twitter/Slack/Facebook/WhatsApp 机器人分享链接时显示的内容:按照这些爬虫使用的同一回退链解析标题、描述和图片(Open Graph → Twitter Card → meta/<title> 简单回退),并提示预览何时会降级。

cargo run -- social-lint ./mi-pagina.html
cargo run -- social-lint https://mi-sitio.com
  • 错误(退出代码 ≠ 0):没有任何途径能解析出标题(既没有 og:title,也没有 twitter:title 或 <title>)——大多数机器人只会显示裸 URL。
  • 警告:没有图片(og:image/twitter:image);没有可解析的描述(meta/OG/Twitter 中没有,页面文本中也没有)。
  • 信息:描述超过约 200 个字符(大多数平台会在此处截断);解析出的图片不是绝对 URL(许多爬虫不会根据页面解析该 URL,而是直接丢弃图片)。

为此,kite-lite 开始捕获 <head> 中的 <meta>(Page.meta);此前这些内容会和 <head> 的其余部分一起被完全丢弃。

页面 SEO 审计器

kite-lite seo-lint <url|page.json|archivo.html> [--json] — linters 的第四位成员,用于基础 SEO 检查。不会重复 a11y-lint 中的标题检查(多个 <h1>、层级跳跃)。

cargo run -- seo-lint ./mi-pagina.html
cargo run -- seo-lint https://mi-sitio.com
  • 错误:完全缺少 <title>;<meta name="robots"> 包含 noindex(页面被明确排除在索引之外——很容易忘记在生产环境中关闭)。
  • 警告:<title> 少于 10 个或多于 60 个字符;没有 <meta name="description">;描述少于 50 个或多于 160 个字符;页面中没有任何 <h1>。
  • 信息:页面少于 200 个单词(内容单薄)。

在 VPS 上部署

经过测试的部署使用多阶段镜像和非特权用户:

docker build -t kite-lite:dev .
docker run --rm \
  --memory=32m --cpus=0.2 --pids-limit=16 \
  --read-only --tmpfs /tmp:rw,noexec,nosuid,size=16m \
  --cap-drop=ALL --security-opt=no-new-privileges \
  kite-lite:dev cdp 0.0.0.0:9222

CDP 端口必须保持在 loopback 上,或通过隧道和身份验证进行保护。不建议直接将其暴露到 Internet。

最低资源(实测,而非估算)

在实际 VPS 上(Docker、cgroup v2、memory.peak,目标为 https://example.com)测得,通过逐步调整 --memory/--cpus,直到出现故障:

模式实际内存峰值
fetch / --js(不渲染)~4.1–4.2 MiB
--png~4.5 MiB
--pdf~5.3 MiB
serve 空闲时~4.0–4.4 MiB
serve 执行一次 parse+render png 后~6.0–7.0 MiB
cdp 空闲时~4.0 MiB
mcp(initialize + fetch_page)~3.9–4.5 MiB

无法将限制降到 6 MB 以下,因为 Docker 本身会拒绝("Minimum memory limit allowed is 6MB")——这不是 kite-lite 的下限,而是容器引擎的下限。生产环境中的进程(测量时已实际运行 7+ 小时)也证实了这一点:限制为 256 MB 时,memory.peak 为 4.46 MiB,也就是超额配置约 55 倍。

--cpus 配额不会影响任何结果的正确性——即使在 --cpus=0.01(一个核心的 1%)下,渲染也能在约 11 秒内完成且不出错;在这种负载下,CPU 影响的是并发时的延迟,而非功能是否正常。--pids-limit=16 绰绰有余:空闲时进程使用 4 个线程(tokio 运行时会按主机核心数启动线程,与 --cpus 无关)。

上面的 32m/0.2 是建议的最低配置,并留有余量(不是精确的 6 MB/6 MiB 下限),可用于处理比简单示例页面更重的页面——更大的 DOM、更高分辨率的 PNG。对于生产环境中的大型页面,请先用你自己的内容重新测量,再调低限制。

后续层

原始路线图(隔离的 fetcher/JS、URL/ Cookie /重定向、最小布局、交互、PNG/PDF、CDP 兼容性、MCP 服务)已全部完成,此外还在之后添加了对声明式 WebMCP 的支持。剩下的最大限制是:目前没有既能解决问题又不破坏本项目隔离模型的方案:kite-lite 无法查看依赖 JavaScript 渲染的内容(SPA、通过客户端 fetch 加载的内容)——这是没有与 JS 绑定的实时 DOM 的直接代价,也正是实际交互、Playwright 和 WebMCP 命令式 API 都无法运行的原因。路线图中没有这一项,因为要解决它就意味着放弃这一设计决策,而非对其进行扩展。

评论

支持 Markdown

第一次接触 MCP?

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

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