内容
# zotero-mcp
一款独立的、远程的MCP服务器,提供对Zotero库的完全读写访问权限 — 搜索、添加、标记、更新、删除和移动项目;创建、重命名和删除集合;上传/下载附件和读取其提取的全文;读取和写入项目笔记;管理标签、回收站和全局搜索库;以及查找Zotero自己的项目类型/字段模式。总共36种工具 — 参见下面的[工具](#工具)以获取完整列表。
## 为什么存在
仅限只读的工具,可以安全地在报告时停止 — 它们永远不需要写回任何内容。该项目故意进一步:对Zotero库进行完整的CRUD(创建、读取、更新、删除),包括删除和移动,以便可以自动标记、添加和清理项目。
这是一个刻意的范围选择,它带来了一个真正的风险:任何写入更改现有项目的**键**(删除、移动到另一个库、“清理库”重置)都会破坏通过Zotero Word插件的实时字段代码引用它的Word文档 — 在执行删除/移动之前,请参见下面的“键安全”。
## 键安全(执行删除/移动前请阅读)
通过Zotero Word插件在Word文档中引用的任何Zotero项目都通过该项目的**键**引用,嵌入到实时字段代码中。保留项目键的操作(创建、更新字段、添加/删除标签、添加笔记)是安全的。不会保留的操作(删除、以及库间移动,Zotero实现为删除+重新创建)将静默地破坏这些引用 — Word文档不会出错,它只会在有人更新字段或下次打开Zotero时显示过时/损坏的字段文本。
尽管存在这种风险,但本项目仍故意选择完整的CRUD。在实现删除/移动工具时:
- 在工具名称和文档字符串中明确破坏性意图(MCP客户端的模型在调用前会读取两者),不仅限于本README。
- 考虑要求调用者传递项目的当前Zotero `version`(乐观并发),以便删除/更新不会静默地覆盖从Zotero桌面应用程序或其他客户端并发进行更改。
- 删除的试运行/确认步骤值得考虑,但由构建该工具的人决定,不在此处决定。
## 配置
要连接的库是一个设置,而不是硬编码的 — 该服务器应该适用于其部署所指向的任意Zotero库。
```
ZOTERO_LIBRARY_ID 数值库ID(用户或组)
ZOTERO_LIBRARY_TYPE "user" 或 "group"(默认:user)
ZOTERO_API_KEY 来自Zotero -> 设置 -> 安全 -> 应用程序
(需要写入权限,而不仅仅是读取)
```
## 部署
作为其自己的Docker容器(`docker-compose.yml`)交付,旨在位于反向代理后面,该代理终止TLS并转发到容器在本地主机上的端口。默认情况下远程/托管,而不是本地stdio服务器 — MCP客户端只需指向URL,无需在本地安装或运行任何内容。
**访问由应用程序本身内置的真实OAuth 2.1授权服务器控制**(`app/oauth_provider.py`),而不是HTTP基本身份验证。 这是一个刻意的设计选择:Claude Desktop/claude.ai的“添加自定义连接器”UI是OAuth-first — 它始终尝试对新服务器进行OAuth发现+授权代码流程,而一个简单的401在服务器前面(基本身份验证会产生)会被读取为“此服务器需要OAuth”,并且在遇到不存在的`/authorize`端点时失败。 实现一个真实的(即使是最小的)OAuth服务器使得“添加自定义连接器”能够正常工作。 该服务器只有一个资源所有者,因此`/authorize`不会委托给第三方身份提供商 — 它显示一个由`MCP_AUTH_USERNAME`/`MCP_AUTH_PASSWORD`检查的本地登录表单。 任何MCP客户端都可以动态注册自己(RFC 7591),但完成该登录表单实际上是访问的门槛。 请参见`app/oauth_provider.py`的模块文档字符串以获取完整的流程。 已注册的客户端和颁发的令牌持续到`MCP_DATA_DIR`(一个Docker卷),因此重新部署不会注销连接的客户端。
`.env`在主机上(不在此存储库中)保存`ZOTERO_LIBRARY_ID`/`ZOTERO_LIBRARY_TYPE`/`ZOTERO_API_KEY`以及`MCP_AUTH_USERNAME`/`MCP_AUTH_PASSWORD`/`MCP_PUBLIC_URL`,通过`docker-compose.yml`的`env_file:`使用。
`.github/workflows/deploy.yml`自动重新部署到已设置的托管主机:仅限手动触发(`workflow_dispatch`,而非推送时),首先运行测试套件,然后通过SSH同步存储库并重新构建容器。 它需要自己的GitHub Actions **机密**来部署SSH密钥和目标主机/端口/用户 — 请参见工作流文件以获取完整列表。 使用专用的部署密钥(而非您用于直接/手动访问的密钥),以便在密钥泄露时可以独立撤销。
## 工具
只读:`search_items`、`get_item`、`list_collections`、`list_tags`、`list_trash`、`list_saved_searches`、`list_groups`、`list_item_types`、`list_item_fields`、`list_item_type_fields`、`list_item_creator_types`、`list_attachments`、`get_fulltext`、`download_attachment`、`list_notes`。
`list_groups`列出配置的API密钥帐户所属的组;其`id`同时用作`target_library_id`(`target_library_type="group"`)用于`move_item_to_different_library`。`list_item_type_fields`/`list_item_creator_types`描述了`create_item`/`update_item`将为给定的`item_type`实际接受的内容 — 在验证错误返回时,请检查它们而不是猜测。
安全、保留键的写入:`create_item`、`create_collection`、`create_saved_search`、`update_collection`、`update_item`、`add_tags`/`remove_tags`/`set_tags`、`rename_tag`、`add_to_collection`/`remove_from_collection`、`trash_item`、`restore_from_trash`、`upload_attachment`、`create_note`、`update_note`。
`trash_item`是一个可逆的软删除(使用`restore_from_trash`撤销)— 不像下面的`delete_item_permanently`,除非稍后项目被永久删除或回收站被清空,否则不会破坏Word引用。
破坏性(参见上面的“键安全”):`delete_item_permanently`、`move_item_to_different_library`、`delete_collection`、`delete_tag`、`delete_saved_search`。前三个需要项目的/集合的当前Zotero `version`(首先从`search_items`/`get_item`/`list_collections`获取),如果与服务器的当前版本不匹配,则拒绝调用,而不是静默覆盖并发更改。`delete_collection`会级联到任何子集合(与Zotero自己的“删除集合”行为匹配),但永远不会删除其中记录的项目。`rename_tag`/`delete_tag`作用于库中带有该标签的每个项目,而不仅仅是一个 — 没有每个标签的版本需要检查,因为标签在Zotero的API中不是独立的版本实体。`delete_saved_search`具有破坏性但风险较低:保存的搜索只是一个存储的筛选器,因此删除它永远不会触及项目或引用。
附件文件内容(`upload_attachment`的`content_base64`参数、`download_attachment`的`content_base64`结果)以base64编码传输 — 该服务器是远程的,无法访问调用者的本地文件系统,因此不能像pyzotero本身期望的那样通过本地路径传递原始字节。
## 测试
```
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"
pytest
```
测试永远不会调用实时Zotero库,即使`.env`中有真实凭证:`app/zotero_service.py`(所有Zotero读写逻辑)针对`tests/fakes.py`的内存`FakeZotero`进行测试,`app/mcp_server.py`的工具函数直接针对由该假模支持的`ZoteroService`进行测试(参见`configure_service()`)。
## 状态
**v1.0** — 已部署并正在积极使用,具有对Zotero Web API的项目/集合/标签/回收站/保存的搜索/模式曲面的完全CRUD覆盖(36种工具;参见[工具](#工具))。直接添加为远程MCP连接器(例如Claude Desktop/claude.ai的“添加自定义连接器”,只需服务器的公共URL)— 上述OAuth流程在浏览器中提示输入用户名/密码,无需手动配置标头。
## 贡献
参见 [CONTRIBUTING.md](CONTRIBUTING.md)。
## 安全
参见 [SECURITY.md](SECURITY.md) 以获取威胁模型和如何报告漏洞。
## 许可证
[MIT](LICENSE)