跳转到主要内容

这是本节的多页打印视图。 .

返回本页常规视图.

集成

本地 API、MCP、系统服务、全局快捷键和 crib:// 深链接。

1 - 本地 API

让本机脚本和工具通过 HTTP 搜索、读取 Crib 中已启用的文档:启用方法、发现文件、密钥与服务端验证、接口和一个 Python 示例。

本地 API 默认关闭。启用后只监听 127.0.0.1,只读,只能访问当前已启用的资料包。笔记、书签、阅读历史、片段和更正都不对外开放。Crib 自己的搜索和阅读不经过这个接口。

启用

  1. 打开“设置 → 集成”,在“本地 API”中打开“启用本地 API”。
  2. “端口”填 0 自动选择可用端口(默认),或指定 1024–65535 中的一个,再点“应用”。
  3. 启动后,设置页显示“地址”,并提供“复制 Schema 地址”“复制密钥”“更换密钥…”。

关闭开关或退出 Crib 后停止监听。资料库不可用时 API 不会启动,设置页显示原因和“重试”。

发现文件

客户端从用户目录读取地址和密钥,不要缓存自动分配的端口:

~/Library/Application Support/Crib/api/

  • status.jsonbase_url、port、pid、instance_id、access_key_file、api_version
  • access-key访问密钥
  • server.lock正在服务的实例持有这把锁
  • api/ 目录权限是 0700,两个文件是 0600。
  • status.json 在服务就绪后才出现,正常关闭时删除。Crib 异常退出可能留下旧的 status.json;下次启动时,只要没有实例持有 server.lock,就会删掉它。
  • 密钥文件是符号链接、有多个硬链接、属于其他用户或权限公开时,Crib 拒绝使用。
  • “更换密钥…”让旧密钥立即失效。随 App 附带的 MCP 程序自动使用新密钥;手动复制过密钥的工具需要重新复制。

先验证服务端,再发送密钥

Crib 崩溃后,原来的端口可能被其他本地进程占用。发送密钥之前,客户端必须确认对端持有同一把密钥:

  1. 检查 status.json 中的 pid 进程仍然存在。
  2. 不带任何凭据请求 GET /auth/challenge?nonce=<nonce>,nonce 是 32 个随机字节的 64 位小写十六进制。
  3. 服务端返回 {"algorithm":"hmac-sha256","proof":"<64 hex>","version":1}。
  4. 客户端计算 HMAC-SHA256(key = 访问密钥的 ASCII 字节, data = "crib-local-api/server-proof/v1\n127.0.0.1:<端口>\n<nonce>"),用常量时间比较与 proof 是否相等。相等才发送密钥。

端口写进了证明,占用端口的进程无法把挑战转发给别处的真实 Crib 来冒充。返回 404 的服务端应当拒绝,不能默认信任。

请求规则

  • /health、/schema、/auth/challenge 不需要密钥;其余接口需要 Authorization: Bearer <访问密钥>。密钥不要放进查询串。
  • 只支持 GET。响应是 JSON,带 Cache-Control: no-store,发送后关闭连接。
  • Host 必须是 127.0.0.1:<端口> 或 localhost:<端口>。带 Origin 头的请求返回 403,所以网页里的脚本不能调用它。
  • 查询串按表单编码解析:+ 是空格,字面加号写成 %2B。未知参数、重复参数、非法转义都返回 400。

接口

路径作用
GET /health服务状态与 API 版本
GET /schemaOpenAPI 描述
GET /auth/challenge服务端身份证明,见上文
GET /docsets/list当前搜索世代中已启用的资料包、版本、语言、摘要和修订
GET /search在已启用的资料中搜索
GET /sections/{key}读取一个章节的纯文本,必须带 generation

搜索参数

text , string
搜索文本,支持 Crib 的过滤语法和 PG96: 这类版本前缀;文本里的过滤优先于下面的参数
project , string , defaultpostgresql
项目
branch , string , default18
版本线;* 表示所有已启用版本
locale , string , defaultzh-Hans
zh-Hans 或 en;* 表示所有语言
domain , string
文档领域
collection , string
Wiki 集合
limit , integer , default20
每页条数,1–50
offset , integer , default0
偏移,0–10000
generation , string
期望的搜索世代

兼容 Dash 的参数名 query、lang、max_results 分别等同于 text、locale、limit。同时给出一对时,值相同可以接受,值不同返回 400。请求的版本或语言没有安装或启用时返回错误,不会悄悄换成默认版本。

每条结果带 section_key、document_key、path、fragment、docset、pack_digest 等字段,以及两个地址:section_url 是带 generation 的章节接口路径,load_url 是在 Crib 中打开该章节的 crib:// 链接。

响应里的 has_more 和 next_offset 用于翻页;timed_out、cancelled、candidate_truncated 为真时结果不完整,不能当作完整结果使用。

读取章节

GET /sections/{key}?generation=<id>&offset=0&limit=16000

  • offset 和 limit 按 Unicode 标量计数,不是字节,也不是 UTF-16 单元。默认 16,000,最大 50,000。
  • 跟随 next_offset 继续读,直到它为 null。
  • 启用、停用或更新资料包后搜索世代会变。用旧 generation 请求返回 409 generation_changed,重新搜索后再读。

时间限制

  • 每个请求有 3 秒执行预算。搜索本身超时返回 200 和 timed_out=true,带上已完成的部分结果;准备阶段就耗尽预算返回 504。
  • 传输层另有 7 秒处理超时、8 秒连接截止;2 秒内没发完请求头的连接被关闭。最多同时 32 个连接。

错误码

状态码含义
400参数或查询无效
401缺少密钥或密钥错误
403来源受限,例如带 Origin 头
404路径或章节不存在
409搜索世代已变,或请求的范围没有安装、启用
503资料库不可用,或请求被取消
504执行或传输超时

响应中的 error 和 message 说明具体原因。

Python 示例

示例从发现文件读取地址和密钥,先验证服务端,再搜索并读取第一条结果。密钥不出现在终端或进程参数里。

from pathlib import Path
import hashlib, hmac, json, os, secrets
import urllib.parse, urllib.request

user_root = Path.home() / "Library/Application Support/Crib"
status = json.loads((user_root / "api/status.json").read_text())
key = Path(status["access_key_file"]).read_text().strip()
client = urllib.request.build_opener(urllib.request.ProxyHandler({}))

def verify_server():
    os.kill(status["pid"], 0)  # 进程不存在时抛出异常
    nonce = secrets.token_hex(32)
    with client.open(status["base_url"] + "/auth/challenge?nonce=" + nonce, timeout=8) as r:
        reply = json.load(r)
    port = urllib.parse.urlsplit(status["base_url"]).port
    message = f"crib-local-api/server-proof/v1\n127.0.0.1:{port}\n{nonce}".encode()
    expected = hmac.new(key.encode(), message, hashlib.sha256).hexdigest()
    if reply.get("algorithm") != "hmac-sha256" or not hmac.compare_digest(reply.get("proof", ""), expected):
        raise SystemExit("not a genuine Crib listener; key not sent")

def get(path):
    request = urllib.request.Request(status["base_url"] + path,
                                     headers={"Authorization": "Bearer " + key})
    with client.open(request, timeout=8) as r:
        return json.load(r)

verify_server()
query = urllib.parse.urlencode({"text": "SELECT", "locale": "zh-Hans", "limit": 3})
result = get("/search?" + query)
for hit in result["results"]:
    print(hit["name"], hit["branch"], hit["locale"])
if result["results"]:
    section = get(result["results"][0]["section_url"] + "&limit=1000")
    print(section["body"])

与 Dash API 的差异

Crib 与 Dash 8.1.1 的本地 API 都有健康检查、Schema、资料包列表和搜索入口,并兼容 query、max_results 参数名。Crib 不支持 Dash 的 docset_identifiers、search_snippets 和 /docsets/enable_fts,默认范围、结果格式和分页上限也不同。依赖完整 Dash API 的客户端不能直接换成 Crib。

2 - MCP

用随 App 附带的 CribMCP 程序,让支持 MCP 的编辑器和 AI 客户端搜索、读取 Crib 中的文档:配置方法、三个工具、分页和搜索世代规则。

Crib 随 App 附带一个 stdio MCP 程序 Crib.app/Contents/MacOS/CribMCP,不需要安装 Python、uv 或 Node。它通过本地 API 只读地搜索和读取已启用的文档。

还没有在真实客户端中走通

协议已用官方 Python SDK 和一个本地 stdio 客户端验证:工具列表、搜索、读取、分页、别名和错误路径都通过。但还没有在任何真实编辑器或 AI 客户端中完成端到端验收。VS Code 已识别配置并显示 Start,工具发现、搜索和读取尚未完成验证。

前提

  • Crib 正在运行。
  • “设置 → 集成”中已启用本地 API。

MCP 程序不会自动启动 Crib,也不会替你启用 API。条件不满足时,工具调用返回 local_api_unavailable 和一句说明。

添加配置

  1. 打开“设置 → 集成”,在“MCP”中把“客户端”选为“通用”或“VS Code”。
  2. 点“复制配置”,粘贴到客户端的 MCP 设置里;VS Code 粘贴到工作区的 .vscode/mcp.json。已有配置时只合并 crib 条目,保留其他服务器。
  3. 客户端用其他配置格式时,点“复制命令”,从中取可执行文件路径和参数。

复制的内容已经带好实际路径,不含访问密钥。注意:

  • JSON 里要写绝对路径,~ 不会被展开。
  • 移动 Crib.app 或换用别的用户目录后,需要重新复制。
  • Crib 不会修改任何客户端的配置文件。
  • 设置页显示“此版本不含 MCP 程序”时,说明这个构建没有附带 CribMCP。

安装在 /Applications、使用默认用户目录时,“通用”格式如下(<用户主目录> 换成实际路径):

{
  "mcpServers": {
    "crib": {
      "command": "/Applications/Crib.app/Contents/MacOS/CribMCP",
      "args": ["--user-data", "<用户主目录>/Library/Application Support/Crib"]
    }
  }
}

“VS Code”格式用 servers,并带 "type": "stdio":

{
  "servers": {
    "crib": {
      "type": "stdio",
      "command": "/Applications/Crib.app/Contents/MacOS/CribMCP",
      "args": ["--user-data", "<用户主目录>/Library/Application Support/Crib"]
    }
  }
}

不传 --user-data 时,程序使用当前用户的 ~/Library/Application Support/Crib。

工具

工具作用
list_installed_docsets列出已启用的资料包、版本、语言和当前搜索世代;可传入期望的 generation
search_documentation在已启用的手册和 Wiki 中搜索
load_documentation_page用搜索结果中的 section_key 和 generation 读取一个章节的纯文本

search_documentation 的参数:

text , string , required
搜索文本,最多 256 个字符;支持 Crib 的过滤语法和 PG96: SELECT 这类版本前缀,文本中的过滤优先
project , string
默认 postgresql
branch , string
版本线,如 18、9.6;默认 18,* 表示所有已启用版本
locale , string
zh-Hans 或 en;默认简体中文,* 表示所有语言
domain , string
文档领域
collection , string
Wiki 集合
limit , integer
每页条数,1–50,默认 20
offset , integer
偏移,0–10000
generation , string
期望的搜索世代

load_documentation_page 的参数:

section_key , string , required
搜索结果里的 section_key
generation , string , required
同一次搜索返回的 generation
offset , integer
起始位置,按 Unicode 标量计数,默认 0
limit , integer
最多返回的 Unicode 标量数,默认 16,000,最大 50,000

每个工具同时返回 JSON structuredContent 和等价的文本内容。

分页与搜索世代

  • 搜索结果用 has_more 和 next_offset 翻页。timed_out、cancelled、candidate_truncated 为真时结果不完整。
  • 读取章节时跟随 next_offset,直到它为 null。
  • 搜索和列表都返回 generation。启用、停用或更新资料包后搜索世代会变,旧坐标的读取返回 generation_changed 错误;重新搜索,用新的 section_key 和 generation 再读。

安全与限制

  • 每次调用都重新读取 api/status.json 和 api/access-key,所以 API 换了端口也能找到。
  • 发送密钥前,程序检查记录的进程仍在运行,并完成 /auth/challenge 验证;验证失败就不发送密钥,工具返回错误。
  • 拒绝符号链接、权限不安全、所有者不对或有多个硬链接的发现文件。只连接与状态文件一致的 127.0.0.1 地址和非特权端口;不走代理,不跟随重定向,不保存 cookie。单次请求 8 秒截止,响应最大 4 MiB。
  • 工具只读文档:不包含个人资料,不能下载资料包、控制窗口或读取任意 URL。
  • 每行一条 JSON-RPC 消息,单行最多 256 KiB,最多同时处理 8 个文档请求。标准输入关闭后取消全部请求并退出。
  • 支持 MCP 2026-07-28 的 stdio 传输,以及 2025-11-25、2025-06-18、2025-03-26、2024-11-05 的 initialize 握手;后两个版本没有用真实 SDK 客户端验证过。不提供 resources、prompts 和 sampling。

工具名称参照 Dash 的官方 MCP 适配器,参数和结果遵循 Crib 自己的约定:读取用 section_key 加 generation,不接受 Dash 的 load_url。依赖 Dash 全部工具的客户端不能直接换成 Crib。

3 - 系统服务与全局快捷键

在其他应用中选中文字,用“服务”菜单交给 Crib 查找或存成片段;三个可选的全局快捷键;以及哪些已经实测、哪些还没有。

两个服务

在其他应用中选中文字,从应用菜单或右键菜单的“服务”中选择:

服务结果选区上限
在 Crib 中查找打开一个新的 Crib 窗口,用选中的文字(去掉首尾空白)搜索256 个字符
在 Crib 中创建片段打开“设置 → 片段”,新建一个未保存的片段草稿,正文与选区完全一致,缩进和换行都保留1,000,000 个字符

Crib 只读取系统传给服务的文字,不读取也不改写剪贴板。

启用

  1. 把 Crib.app 放进“应用程序”文件夹。
  2. 在“系统设置 › 键盘 › 键盘快捷键 › 服务”中找到“在 Crib 中查找”和“在 Crib 中创建片段”,勾选启用。也可以在那里给它们指定快捷键。

“设置 → 集成 → 系统服务”里有同样的提示。

Crib 没在运行时

选择服务时如果 Crib 没有运行,macOS 会启动它。窗口准备好之前到达的请求先排队,最多 16 个,准备好后依次处理。

实测范围

2026-10-29 在一个独立测试构建上实测通过:TextEdit 中两个服务的发现、已运行时查找、创建片段、连续两次片段请求、关闭最后一个窗口后查找,以及两个服务的冷启动。当前构建还没有重新实测。

被拒绝的请求

以下请求会被拒绝,不会排队重试:

  • 没有选中文字,或选区只有空白。
  • 选区含有制表符、换行之外的控制字符。
  • 查找文字超过 256 个字符,或片段超过 1,000,000 个字符。
  • 待处理的请求已有 16 个。
  • 片段库是只读的。

被拒绝时,Crib 把原因返回给 macOS,并在 Crib 内记录这条错误。缩小选区或处理完积压后重新选择服务。

待证实:被拒绝时能否看到错误提示

实测时在 TextEdit 中选中 257 个字符再调用“在 Crib 中查找”,没有看到任何错误提示,也无法证明这次请求到达了 Crib。单元测试证明超长输入会被拒绝,但用户能否看到提示还没有验证。

全局快捷键

全局快捷键在“设置 → 通用 → 全局快捷键”中,三项都默认关闭。打开右侧开关启用,点“修改”录制新的组合。

功能默认组合
显示窗口⌃⌥⌘Space
打开搜索⌥⌘Space
搜索选中文字⌃⌥⌘D
  • “搜索选中文字”需要辅助功能权限。设置页会显示“授予权限…”和“重新检查”。读不到选中文字时,可以改用服务菜单里的“在 Crib 中查找”。
  • 组合被系统或其他应用占用时,设置页显示“…已被系统或其他应用占用,请换一个组合。”和“重试”。
  • 快捷键按当前键盘布局解析,切换输入法后重新注册。不支持数字小键盘上的键;按下 ⌘ 后会变成其他字符的键也会被拒绝。
实测范围

设置页的开关和录制在早期测试构建上实测通过。在其他应用中实际按下快捷键呼出 Crib,还没有实测。

4 - 深链接

用 crib:// 链接从笔记、其他应用或脚本直接打开 Crib 中的某一章节:怎样复制链接、两种链接格式、打不开时的提示,以及旧的 pgdash:// 链接。

Crib 注册了 crib 协议。点击一个 crib:// 链接,Crib 会打开它指向的章节。界面里把这类链接叫“链接”。

复制链接

  • 在搜索结果上点右键,选“复制链接”。折叠起来的多版本结果,复制的是代表项那一条。
  • 阅读时,从正文上方的“更多”菜单(版本、书签按钮右侧)选“复制链接”,复制正在阅读的章节。
  • 本地 API 搜索结果中的 load_url 也是这种链接,见本地 API。

复制得到的链接形如:

crib://open?project=postgresql&branch=18&locale=zh-Hans&section=<章节键>&manifest=<资料包摘要>

manifest 记录结果来自哪个资料包。搜索结果和阅读器各自用自己所属的搜索世代生成链接,不会把一个版本的结果链到另一个版本。

两种格式

crib://open

project , required
项目,如 postgresql
branch , required
版本线,如 18、9.6;Wiki 为空
locale , required
zh-Hans 或 en
section
章节键,必须精确匹配
manifest
资料包摘要,64 位小写十六进制;给出时必须与已启用资料包的摘要一致
concept、semantic
没有 section 时使用,必须恰好匹配一个章节

带 section 时,该章节必须存在于当前已启用的内容中,且项目、版本线、语言都与参数一致。未知参数、重复参数、超过 4096 字节的值都会让链接被拒绝。

crib://section

crib://section/<章节键>
crib://section/<章节键>?manifest=<资料包摘要>

路径只有一段,就是精确的章节键;只允许一个可选的 manifest 参数,不能带片段、用户名、端口或其他参数。“复制链接”目前生成的是 crib://open 格式;这种短格式适合手写,或由脚本从本地 API 返回的 section_key 拼出来。

章节键和资料包摘要都来自签名资料包,详见资料包格式。

打不开时

Crib 只打开链接精确指向的章节,不会退而打开相似的页面或其他版本。打不开时,当前页面保持不变,Crib 显示:

提示原因
无法打开链接:页面地址无效链接格式不对,例如多了参数、路径不止一段、manifest 不是 64 位十六进制
无法打开链接:资料库中没有这个页面或文件链接格式正确,但当前启用的内容里找不到:版本未安装或已停用,或 manifest 指向的修订不是当前启用的那个

版本已停用时,在资料库中启用后再点链接。

说明

Crib 没有运行时点击链接、由链接启动 Crib 的情况还没有实测。Crib 打开后如果没有跳到目标章节,再点一次链接。

旧的 pgdash:// 链接

Crib 仍然注册 pgdash 协议,PGDash 时期复制的 pgdash://open?... 链接按上面相同的规则打开。PGDash 的 pgdash://search 没有实现,会被拒绝。Crib 自己只生成 crib:// 链接。

pgdoc:// 不是给用户的

阅读器内部用 pgdoc://<资料包摘要>/<路径> 加载资料包里的文件。这个协议不向系统注册,不能从其他应用打开,也不要把它当作链接保存。