zotero-cli 技能实战指南:在 shell 中直达 Zotero 文献库的检索、阅读、标注与写作全流程
zotero-cli 技能实战指南:在 shell 中直达 Zotero 文献库的检索、阅读、标注与写作全流程
zotero-cli 是本仓库(zotero-mcp)随包发布的命令行技能,它让具备 shell 访问能力的 AI Agent 或开发者不经过 MCP 服务器,直接读写 Zotero 文献库——从关键词/语义检索、读取 PDF 全文与指定页范围,到设置元数据、管理合集与标签、批量添加条目和导出参考文献。读完本文,你将掌握 zotero-cli 的完整命令体系、--json 机器可读输出契约,以及一套"先定位键(key)、再对键执行动作"的高效工作流,并能结合仓库源码理解其底层实现原理。
为什么需要 zotero-cli:省掉每轮 ~14k token 的上下文开销
zotero-cli 与 Zotero MCP 服务器的定位不同:MCP 服务器的工具 schema 在每次请求时都会占用约 14k token 的上下文(无论你是否真正用到这些工具),而命令行方式"只为你实际运行的那次调用付费"。因此 SKILL.md 明确建议:当你有 shell 访问权限时,优先使用 zotero-cli 而非 MCP 服务器。
该技能本体打包在 wheel 内部,由 zotero-mcp install-skill 安装到当前 Agent 环境中。skill_install.py 的模块注释解释了它的分发策略:不同 Agent 读取约定不同——Claude Code 读取 skills 目录下的 SKILL.md,Cursor 与 Windsurf 读取带各自 frontmatter 的规则文件,而 Codex、Amp、OpenCode、Jules、Gemini 等工具读取单文件项目级指令。install-skill 通过检测磁盘上已有的目录(如 .cursor/、AGENTS.md)自动判断目标,而不是要求用户学习这些工具的"分类法"。安装过程遵守两条铁律:绝不覆盖(目标已存在且内容不同时只报告、不覆写,除非传入 --force)和渐进式披露(共享指令文件只写一小段指针块,完整技能正文放在旁边文件,避免把约 1400 token 的技能全文塞进每个 Agent 的常驻上下文)。
首次调用前的环境检查
在第一次真实调用之前,先确认环境可用:
zotero-cli config # 打印解析后的 Zotero 设置
该命令在源码中的实现(cli_standalone.py)会调用 setup_zotero_environment(),然后汇总所有 ZOTERO_ 前缀环境变量以及 OPENAI_API_KEY、GOOGLE_API_KEY、GEMINI_API_KEY;默认对 API key 做脱敏显示,只有加 --show-secrets 才显示完整密钥。
如果 config 失败,说明 Zotero 不可达,需要区分两种模式:
- 本地模式:Zotero 桌面应用正在运行且已启用本地 API;
- Web 模式:需要设置
ZOTERO_API_KEY与ZOTERO_LIBRARY_ID环境变量。
遇到这种情况应如实说明 Zotero 不可达,而不是去猜测库中内容。
机器可读输出:--json 信封契约
默认输出是给人看的 Markdown;--json 则保证每次调用输出一个形状稳定的 JSON 对象,让你永远不必解析散文:
{"ok": true, "command": "search", "schema": 1, "data": {...}}
{"ok": false, "command": "search", "schema": 1, "error": {"message": "...", "code": "..."}}
成功与失败的信封都写到 stdout,[INFO]/[WARN] 诊断信息写到 stderr。使用时先检查 ok 再使用 data。运行 zotero-cli --json-schema 可查看完整的输出契约文档。
这一契约的源码实现在 cli_json.py:envelope() 统一构造 {"ok", "command", "schema": 1} 形状,SCHEMA_VERSION = 1 表示信封自身结构的版本(各命令向 data 中新增字段属于兼容的增量变更,删除字段则不允许)。值得注意的是 emit_error() 的注释刻意把失败信封也写到 stdout——调用方只读一个流时,不必合并两个流才能发现调用失败;而人类可读路径则把错误写到 stderr,这对终端用户更合适。
另一个关键设计在 _out()(cli_standalone.py):底层工具多数以散文形式返回失败而非抛出异常,若不处理,这些散文会变成 ok: true 的信封并以退出码 0 结束,让检查 ok 的调用方误以为写操作成功。因此 _reports_failure() 用固定开头的正则(Error、Failed to、Could not、Cannot)识别失败散文,在 JSON 模式下转为 ok: false 信封并 sys.exit(1)。cli_standalone.py 的 main() 还保证异常同样以统一形状输出,且 ZOTERO_CLI_DEBUG 环境变量可触发 traceback 输出。
三级条目投影:keys_only / summary / full
任何列出条目的命令都返回相同的条目形状(project_item),因此针对 search 写的管道同样适用于 get recent 或 get collection-items:
keys_only:key、itemType、title、date——足以决定下一步取什么,且小到可以列整个库;summary(默认):在 keys_only 基础上增加作者、DOI、出版物、标签、所在合集;full:再增加摘要和完整的原始data字典(放在raw下)。
每级都携带 key 与 itemType,方便调用方识别手中对象;若条目在回收站,会额外带 "deleted": true。相关测试见 test_cli_json_output.py 与 test_library_code_keeps_stdout_clean.py(后者专门守护 stdout 纯净性,确保 zotero-cli --json 系列绝不输出非 JSON 内容)。
核心工作循环:find keys → act on keys
几乎所有任务都遵循同一模式:先找到条目键,再对键执行动作。条目键(item key)是 8 个字符,是每个命令之间传递的"货币"。
# 1. find - 用 --detail keys_only 在浏览时保持结果小巧
zotero-cli --json search "attention mechanisms" --limit 10 --detail keys_only
# 2. act - 取元数据、全文,或一个页范围
zotero-cli --json get metadata ABCD1234
zotero-cli --json get fulltext ABCD1234
zotero-cli --json read ABCD1234 --start-page 3 --end-page 8
把键直接管道给下一个调用:
zotero-cli --json search "diffusion models" --limit 5 --detail keys_only \
| jq -r '.data.items[].key' \
| while read -r key; do zotero-cli --json get metadata "$key"; done
这个循环的 JSON 路径很有意思:cmd_search 在 JSON 模式下并非重新实现一遍选择逻辑,而是先调用与 Markdown 模式完全相同的工具函数获取结果文本,再用 _keys_from_markdown() 从固定格式的 **Item Key:** KEY 行中把键读回来(cli_standalone.py)。这样搜索级联、语义排序、合集限定等逻辑只存在一份,JSON 只改变渲染方式,两种模式在"哪些条目命中"上永远一致。_fetch_projected() 还会按给定顺序重新投影条目,因为顺序本身承载意义(相关性、时间新近度)。
选择正确的搜索模式
search(别名 s)提供六种模式,按需选择:
| 模式 | 适用场景 | 命令示例 |
|---|---|---|
items(默认) |
你知道标题、作者或某个短语 | search "Vaswani attention" |
semantic |
只关心主题或想法,没有确切措辞 | search --mode semantic "why transformers scale" |
tag |
按标签筛选已归档条目 | search --mode tag "to-read,important" |
advanced |
结构化字段条件组合 | search --mode advanced --conditions '[...]' |
citekey |
按 BibTeX 引文键查找 | search --mode citekey smith2020 |
notes |
搜索笔记正文(而非条目字段) | search --mode notes "research question" |
semantic 模式依赖搜索索引:用 zotero-cli db status 检查、zotero-cli db update 构建。若索引为空,应回退到 items 模式并说明原因,而不是报告"无结果"。
items 模式还支持 --qmode 参数(titleCreatorYear,默认,或 everything),以及 --sort-by、--sort-direction、--collection 限定范围。advanced 模式通过 --conditions 传入 JSON 数组、--join-mode 指定 all/any 连接。semantic 模式可用 --filters 传 JSON 过滤器。
跨库搜索:--all-libraries
默认每次搜索只覆盖当前活动库。当你不确定条目在哪个库、或单库搜索落空但条目可能存在于群组库时,加 --all-libraries:
zotero-cli search "Cladder-Micus" --all-libraries
此时每个结果会标注 **Library:** <name>。该选项需要 SQLite 后端(本地模式默认),未启用时会明确报错,因此先试一次,被拒绝就回退到逐个库搜索。标签过滤可以与它配合;--collection 不行,因为合集只存在于某一个库内。
高效阅读:outline 定位 + read 页范围
对一本书那么长的 PDF 执行 get fulltext 会返回海量文本。当你只需要某一段内容时,先读目录(outline)定位,再只读那几页:
zotero-cli --json outline ABCD1234
zotero-cli --json read ABCD1234 --start-page 42 --end-page 55
对于"我的库对 X 说了什么"这类问题,优先 search --mode semantic 然后定向 get metadata,而不是通读整篇论文。
read 命令(cli_standalone.py)在 JSON 模式下返回 {item_key, start_page, end_page, text, chars}。文本模式走 read_pdf_text(..., surface="cli");--end-page 默认等于 --start-page(即只读一页),且会自动钳制到最后一页。
数学、图表和表格:文本不可靠时的图片模式
提取出的文本对正文(prose)可靠,对数学公式、图和表不可靠——符号会丢失、表格单元格会粘连在一起。read 会在发生这种情况的每一页上标注,例如 "Garbled in this text: Equation (1), Table 2"。对这些页,如果你能看图,就直接看页面本身:
zotero-cli read ITEM_KEY --start-page 4 --format image # 每页一张 PNG,最多 10 页
zotero-cli read ITEM_KEY --start-page 4 --format image --rect 0.35,0.49,0.3,0.05 # 放大到某个区域
--rect 只与 --format image 搭配(源码中会以 bad_rect 错误拒绝其他组合),坐标为归一化 0–1 的 x,y,width,height;图片写入 --out 指定目录,默认是新的临时目录(tempfile.mkdtemp(prefix="zotero_pages_")),每张 PNG 命名为 <item_key>-p<页码>.png,带 --rect 时后缀 -region。JSON 模式返回每张图片的路径、宽高和页码。
分页:处理超过 --limit 的列表
列表类命令以 --limit 封顶。当还有更多数据时,响应会提示并给出下一个 offset:
zotero-cli --json get collection-items QS7TQPPA --limit 100 --offset 100
持续翻页,直到 data.count 小于 --limit 为止。注意 get collection-items 的 --detail 默认是 summary,--limit 默认 50;get collections 与 get tags 的 --limit 默认 500,get recent 默认 10,get feed-items 默认 20。
写入操作:add、edit、notes 与 batch
写命令把操作结果以文本形式放在 data.text 下:
zotero-cli add doi 10.1038/s41586-021-03819-2 -c "Reading List"
zotero-cli edit ABCD1234 --title "Corrected Title" --add-tags reviewed
zotero-cli notes create --item-key ABCD1234 --text "Key finding: ..."
zotero-cli batch --item-keys A1B2C3D4,E5F6G7H8 --add-tags screened
add 有五个子命令,覆盖常见入库路径:
add doi <doi>:按 DOI 入库(还有add url、add isbn);add file --filepath:导入本地文件,--item-type默认document,可用--title覆盖元数据提取失败的情况;add bibtex:--bibtex内联或--file指定 .bib/.bibtex 文件;add csl-json:--json内联或--file指定 .json/.csljson 文件。
所有 add 子命令共享 --collections(逗号分隔的键/名称/路径)、可重复的 -c/--collection(不按逗号拆分,因此含逗号的合集名也能用)、--tags、--create-collections(自动创建不存在的合集,含父子路径)以及 --if-exists 三值选项:
file(默认):条目已存在时复用旧条目并补齐缺失的合集与标签;skip:完全不动已存在条目;duplicate:无论如何都新建条目。
因此 add 默认是幂等的:重复运行只是把已有条目归入指定合集,不会制造重复记录。部分子命令还提供 --attach-mode(auto/linked_url/import_file/none/required)。
在进行破坏性变更(delete、duplicates merge、对大量条目的 batch)之前,先与用户确认并展示将影响哪些条目。delete item 默认拒绝删除笔记(笔记通常是误删),必须显式加 --allow-note。批量更新条目由 batch 承担:除了 --item-keys,还可以用 --query(按搜索词选条目)或 --tag(按标签选)圈定目标,支持 --add-tags/--remove-tags 以及用 --set/--remove-keys 读写 Extra 字段。
本地写入授权
本地模式的写操作需要一次性执行 zotero-mcp authorize-local(Zotero 10 或更新版本),或提供 Web API 凭据。如果写入因这个原因被拒,错误信息会明确指出。
阅读并标注一篇论文:四步工作流
先定位附件键、读文本、必要时再看文件路径:
zotero-cli get children ITEM_KEY # 该条目的 PDF 附件键
zotero-cli read ITEM_KEY --start-page 1 --end-page 99 # 结束页自动钳到最后一页
zotero-cli path ATTACHMENT_KEY # PDF 在磁盘上的路径
标注的正确姿势是:把一切都计划好、检查无误,然后一次性写入。SKILL.md 给出了严格的四步流程:
zotero-cli --json layout ATTACHMENT_KEY:列出图表与公式框及其题注,每个框都带一个可直接粘贴的rect_arg(源码cmd_layout走annotations.detect_layouts(),--pages可指定all/3/3-6/1,4,6-9);- 每行写一个 JSON 对象。三种标注形态:
- 高亮:
{"page": 4, "text": "exact words", "comment": "...", "color": "yellow"}——高亮文本必须从read中逐字复制,系统会在该页及其前后各两页范围内搜索它; - 框选:
{"page": 3, "rect": "x,y,w,h", "comment": "..."}; - 便签:
{"page": 1, "note": "x,y", "comment": "..."}——以某个点为中心放置,文本就是 comment;
- 高亮:
zotero-cli annotations batch --attachment-key ATTACHMENT_KEY --file plan.jsonl --dry-run:先试跑一次,显示每个高亮将覆盖哪些文字,修正所有未命中的地方;- 去掉
--dry-run真正执行。任何未落地的条目会列在data.results中,ok: false,退出码为 1。
批量输入文件 --file 默认 -(读 stdin),既支持 JSON Lines,也支持单个 JSON 数组;_read_annotation_specs() 会逐行校验 JSON 并拒绝空批次(empty_batch)。_annotations_batch() 实现中,各 spec 按附件分组、每组一次调用(PDF 只取一次),即使部分 spec 失败也会逐个尝试全部、逐个列出结果,方便只重试失败项。颜色接受 Zotero 的命名:yellow、red、green、blue、purple、magenta、orange、gray(也接受十六进制,默认 #ffd400)。三四种有固定含义的颜色比八种都好读——建议在条目上加一条笔记说明各颜色含义。
annotations(别名 ann)其余子命令:list(可加 --pdf-extraction,默认按 Zotero 已保存标注)、create(单条创建,与 JSONL spec 同字段)、update(--text/--comment/--color/--add-tags/--remove-tags)、delete。
当结果看起来不对:诊断路径
- 空搜索结果:先检查
zotero-cli config,语义模式还要zotero-cli db status。在确认库确实可达且已建索引之前,不要报告"你没有关于 X 的论文"; ok: false:读error.message,它指出了原因;- 搜索结果带 partial-results 提示:说明扫描被截断,不是"没有其他匹配",收窄查询条件后重跑;
- 条目标记
"deleted": true:说明它在回收站,不要当作活动库内容处理。
完整命令参考与参数速查
reference.md 位于本技能目录 src/zotero_mcp/skills/zotero-cli/reference.md,列出每个命令与全部 flag,由 scripts/gen_skill_reference.py 从 CLI 自身的参数解析器生成(勿手改)。SKILL.md 未覆盖的需求,先查它;任何单个命令也可用 zotero-cli <command> --help。
需要留意的全局规则(reference.md 开头):--json 与 -v 定义在顶层解析器和每个一级命令上,因此可以放在命令名前或后,但不能跟在子命令后面——get --json metadata KEY 能解析,get metadata --json KEY 不行。完整的命令族包括:
- 元数据与全文:
get metadata|fulltext|bibtex|collections|collection-items|children|tags|recent|libraries|feeds|feed-items,outline、read、path、layout、export; - 搜索:
search(六模式)、related(按 DOI/arXiv ID/条目键查引用方向,--direction references|citations|both,默认 20 条)、coverage(按合集统计库覆盖)、duplicates find|merge(--method title|doi|both,合并前建议--dry-run); - 整理:
collections create|update|search|manage、tags(--query/--tag/--add/--remove)、edit(标题、作者 JSON 数组、日期、DOI、URL、Extra、卷期页码等二十余个字段)、batch、library switch|list|reset(--library-id+--library-type user|group); - 库级任务:
db update|status|inspect|batch-status|batch-import(更新索引,--force-rebuild/--limit/--fulltext/--openai-batch)、synthesize(按合集或标签汇总标注,--format markdown|json,默认上限 200 条)、attach(--file上传或--url建链接)、notes list|create|update|delete、delete item|collection|annotation、config。
小结
zotero-cli 把 Zotero 文献库变成了一组可组合的 shell 命令:--json 信封保证机器可读,find keys → act on keys 的工作循环让检索、阅读、标注、写入都能流水线化,六种搜索模式覆盖从精确到语义再到笔记正文的检索需求,而 layout + annotations batch 的四步标注流程让批量标注既准确又可回滚。它既是 MCP 服务器之外更省 token 的替代通道,也是脚本、定时任务与 Agent 集成 Zotero 的统一入口。