evanliu3594/dsh-zotero-plugin ↗★ 0

dsh-zotero-plugin

提供本地Zotero文献库读取与保存工具 适合需要让AI直接检索、阅读和整理本地Zotero文献的科研人员。

套件
dsh-zotero-plugin
相容性
待驗證
版本
0.1.0
授權
MIT
最近更新
2026年10月14日

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:evanliu3594/dsh-zotero-plugin

4. 完全重启 Harness,在对话里即可使用全部 zotero_* 工具


`dsh plugin add` 只让 pnpm `link:` 本目录、**不跑构建**,所以第 2 步不能省;之后每次改 `src/` 都要重新 `pnpm build` 再重启 Harness。包声明了 `dsh.bundle.patch`,CLI 会把包名追加进 profile 的 `dsh.profile.bundles`,因此插件层会自动激活;若忘了 `pnpm build`,profile 加载到的仍是旧产物,`pnpm probe` 会报产物相对源码陈旧。

## 开发

```sh
pnpm install
node scripts/link-harness.mjs     # 依赖链接(见上)

pnpm build        # src → lib/index.js
pnpm probe        # artifact 面:纯 node 按包名加载产物 + 新鲜度 + 工具注册
pnpm typecheck    # 类型面:tsc --noEmit(含 src、scripts 与 tests)
pnpm verify       # source 面:裸 cordis Context + 真实 ToolRuntime 验证 src 注册
pnpm smoke        # 十个工具对真实 Zotero 逐个执行(只读)
pnpm smoke:write  # 额外保存一条明确标注可删除的测试条目

verify、smoke 与 tests/ 下的 TypeScript 用 node --experimental-strip-types 启动,不依赖 tsx:tsx 和 esbuild 都要在管道上起子进程,受限沙箱会拒。为此插件源码保持可擦除语法(无参数属性、枚举、命名空间、装饰器),这样 strip 模式在 harness 支持的每条 Node 线上都能加载,包括移除了 transform 模式的 Node 26。

测试

tests/ 下的门分两类。不需要 Zotero 的:

pnpm test:ab          # 同一假 Zotero 服务器跑两遍:src 面与 lib 面输出必须一致
pnpm test:integrity   # 产物是否出自当前源码,且每个修复留下的标记都还在

需要本机 Zotero 运行的(只读;test:readback 例外,会新建一条测试条目):

pnpm test:contract              # 每个工具经真实 ToolRuntime 调用,验证 harness 输出校验通过
pnpm test:acceptance            # 导出与投影的行为断言(跑 src 面)
pnpm test:acceptance:artifact   # 同上,强制加载 lib/ 产物(打包运行时的入口)
pnpm test:status-empty          # 空文库下 zotero_status 仍被接受
pnpm test:readback              # 保存后读回:标题被 Zotero 改写时仍能取到 key(会写库)

写测试时注意两点:必须经真实 ToolRuntime 调用,直接调 tool.execute() 会绕过 harness 的 lossless-JSON 输出校验,历史上两个默认调用即失败的缺陷正是这样漏掉的;tests/fake-zotero.ts 提供进程内假 Zotero 与 boot(),用于不碰真实库的场景。

源码方式加载(harness 自己的源码启动链路带 tsx,可以直接吃 TS;打包桌面版不行,必须先 pnpm build):

cp cordis.example.yml cordis.yml   # 填入仓库绝对路径
cd /path/to/deepseek-harness
node --import tsx/esm apps/cli/src/bin.ts --profile web --patch /path/to/dsh-zotero/cordis.yml

配置

全部可调参数在 src/config.ts(schema 带默认值),可在 profile 的插件 config 里覆盖:

字段默认说明
host / port127.0.0.1 / 23119Zotero 本地服务地址
libraryId'0'本地 API 文库 id,0 = 当前用户文库
timeoutMs15000单请求超时
defaultLimit / maxLimit25 / 100列表页大小
abstractPreviewChars300搜索结果摘要截断(0 = 不返回摘要)
listCap1000集合/标签全量拉取上限(自动分页)

安全模型

  • 文献库内容一律视为数据而非指令:笔记经 HTML 清洗为纯文本后再进入模型上下文。
  • 唯一的写工具 zotero_add_item 一次只保存一条;保存后读回校验,读回不到 key 时明确返回 readBackFailed: true,不把未确认的保存说成成功。
  • 没有更新/删除类工具——Zotero 本地 API 本身只读,模型无法绕过。

已知限制

  • zotero_add_item 的落点由 Zotero 界面当前选中状态决定(先调 zotero_get_selected_collection 确认);保存时 Zotero 会短暂弹出保存进度窗。
  • zotero_add_item 不提供 language 参数:CSL 语言字段由 Zotero 自己填(该保存路径下它丢弃外部传入的值并写入应用默认值),因此本工具无法设置条目语言。
  • zotero_add_item 保存后按标题读回条目来取得 key;Zotero 会对标题做书写体归一化(例如句中的 Zotero 会被小写),所以比对按大小写不敏感进行。若仍定位不到,返回 readBackFailed: true 与 hint,条目本身已保存。
  • zotero_list_tags 与 zotero_list_collections 默认只返回 defaultLimit(25)条,total 是全库数量;需要完整清单时显式传 limit。
  • Zotero 的 CSL-JSON 导出体里不含条目 key(id 是引用键)。因此该格式只能逐条请求,且无法在返回体里反查条目 key;BibTeX 导出不带该限制。
  • zotero_get_bibtex 传了不存在的 key 时,failedKeys 会报出来。Zotero 接受但不导出成参考文献条目的 key(笔记、附件)只在 BibTeX 下表现为 count 偏小;CSL-JSON 会把它们导出成 document 条目。
  • 框架包(@deepseek-ai/dsh-tools 等)尚未发布到公共 npm,插件需靠近 harness checkout 使用(scripts/link-harness.mjs 负责链接);包发布后此步骤可删。

致谢

License

MIT

配置

全部可调参数在 src/config.ts(schema 带默认值),可在 profile 的插件 config 里覆盖:

字段默认说明
host / port127.0.0.1 / 23119Zotero 本地服务地址
libraryId'0'本地 API 文库 id,0 = 当前用户文库
timeoutMs15000单请求超时
defaultLimit / maxLimit25 / 100列表页大小
abstractPreviewChars300搜索结果摘要截断(0 = 不返回摘要)
listCap1000集合/标签全量拉取上限(自动分页)