使用说明
Zotero MCP
本地MCP服务器,为AI代理提供对Zotero API的安全、结构化访问(包括Docker封装)。
该项目的目标是Docker MCP目录提交流程,一旦发布的映像可用,它将以自制映像的形式发布。
状态:实现了核心工具(搜索、获取、创建、上传、收集)。可靠性(重试+读取缓存)和结构化日志记录已到位。测试存在,但需要依赖关系;Dockerfile已存在,本地映像验证正在等待中。 已知问题: zotero_add_item_to_collection 是不稳定的,需要调查。
**视觉(v1)**代理人可以在你的个人Zotero图书馆里搜索一篇论文。如果缺少,代理可以将论文及其元数据(包括文件附件,通常是PDF)上传到您的库中。
非目标(v1):组库、协作功能和高级Zotero工作流程(笔记同步、引用样式等)不在第一个版本的范围内。
运输: stdio 仅限(MCP本地服务器)。
运行时:Python 3.11以上。包装: uv + pyproject.toml.入口点: python -m zotero_mcp.
MCP-SDK:使用官方MCP Python SDK(mcp)与 stdio 运输 src/zotero_mcp/server.py.
功能(v1)
- 列出个人Zotero图书馆的馆藏。(已实施)
- 在个人Zotero库中搜索并列出项目。(已实施)
- 检索项目元数据(包括附件)。(已实施)
- 使用元数据创建新项目。(已实施)
- 上传文件附件(通常是PDF)并将其链接到项目。(已实施)
- 按名称或键将项目添加到集合中。(已实施,但目前不稳定)
MCP工具(v1)
v1的当前刀具表面。以下工具反映了当前的实施情况。
通用规则
- 所有工具仅在个人库中运行。
item_key是Zotero项目密钥(8个字符串)。tags是字符串;重复项将被忽略。creators条目使用Zotero的基本创建者形状:creator_type,first_name,last_name,或name对于单字段创建者。collection_name匹配是不区分大小写的精确匹配;不明确的匹配会返回候选键的错误。- 错误作为MCP工具错误返回,代码如下
ZOTERO_AUTH_ERROR,ZOTERO_NOT_FOUND,ZOTERO_RATE_LIMITED,ZOTERO_VALIDATION_ERROR,ZOTERO_UPSTREAM_ERROR. - 工具结果被包裹在一个标准信封中:
{ "ok": true|false, "data": ..., "error": ... }. - 通过以下方式支持分页输入
start(或offset作为别名)以及next_start当Zotero提供它时,作为回应。
工具: zotero_search_items
- 目的:搜索并列出个人图书馆中的项目。
- 输入
query(字符串,必填):自由文本搜索查询。limit(int,可选,默认值25,最小值1,最大值100):返回的最大项目数。sort(字符串,可选,默认值relevance):排序键,转发给Zotero。如果Zotero拒绝relevance,服务器将重试dateModified并返回sort_used.start(int,可选,默认值0,最小值0):开始偏移到结果集中。offset(int,可选,最小值0):别名start(使用其中之一)。tags(string[],可选):过滤包含所有标签的项目。- 输出
items(数组):带字段的项目列表item_key,version,item_type,title,creators,date,doi,url,abstract,tags,extra.total(int):匹配的项目总数(如果可以从Zotero获得)。next_start(int,可选):如果Zotero提供下一页,则分页光标。sort_used(字符串,可选):在应用回退排序时填充。
工具: zotero_list_collections
- 目的:列出个人图书馆的馆藏。
- 输入
limit(int,可选,默认值25,最小值1,最大值100):返回的最大集合数。start(int,可选,默认值0,最小值0):开始偏移到结果集中。- 输出
collections(array):包含字段的集合列表collection_key,name,parent_key,version,num_items(如果可用)。total(int):总收藏量(如果可以从Zotero获得)。next_start(int,可选):如果Zotero提供下一页,则分页光标。
工具: zotero_get_item
- 目的:获取单个项目的元数据。
- 输入
item_key(字符串,必填)。- 输出
item(对象):带字段的项item_key,version,item_type,title,creators,date,doi,url,abstract,tags,extra,加attachments(附件摘要数组)。
工具: zotero_create_item
- 目的:在个人图书馆创建一个新的书目项目。
- 输入
item_type(string,必填):Zotero项目类型(例如。,journalArticle).title(字符串,必填)。creators(数组,可选,默认[]).date(字符串,可选)。doi(字符串,可选)。url(字符串,可选)。abstract(字符串,可选)。tags(string[],可选)。extra(字符串,可选)。- 输出
item_key(string):已创建项目密钥。version(int):Zotero项目版本。
工具: zotero_upload_attachment
- 目的:将文件附加到现有项目。
- 输入
item_key(字符串,必填):要附加到的父项密钥。file_path(字符串,可选):容器/运行时中文件的本地路径(必须存在且可读)。file_url(字符串,可选):用于获取和上传的http/https URL。file_bytes_base64(string,可选):要上传的base64编码文件字节数。filename(字符串,使用时需要file_bytes_base64):与上传关联的文件名。title(字符串,可选):附件标题(默认为文件名)。content_type(字符串,可选):省略时从文件扩展名推断(回退application/octet-stream).- 提供以下之一
file_path,file_url,或file_bytes_base64. - 输出
attachment_key(string):已创建附件项密钥。version(int):附件项版本。parent_item_key,title,content_type,size如果可用,则包括在内。
工具: zotero_attach_arxiv_pdf
- 目的:获取arXiv PDF并将其附加到现有项目。
- 输入
item_key(字符串,必填):要附加到的父项密钥。arxiv_id(string,必填):arXiv标识符或arXiv URL(例如。,1706.03762v1或https://arxiv.org/abs/1706.03762).title(字符串,可选):附件标题(默认为文件名)。- 输出
attachment_key(string):已创建附件项密钥。version(int):附件项版本。parent_item_key,title,content_type,size,arxiv_id,pdf_url如果可用,则包括在内。
工具: zotero_get_sort_values
- 目的:返回已知的Zotero排序值加上服务器默认值。
- 输入:无。
- 输出
values(string[]):已知的排序键。default(string):服务器使用的默认排序。fallback(string):回退排序在以下情况下使用relevance被拒绝。
工具: zotero_add_item_to_collection
- 用途:按集合键或名称将项目添加到集合中。
- 注:目前呈片状;可能间歇性失败,需要调查。
- 输入
item_key(字符串,必填):要添加的项目键。collection_key(string,可选):要添加的集合键。collection_name(字符串,可选):要解析的集合名称(不区分大小写的精确匹配)。- 输出
item_key(string):已添加项目密钥。collection_key(string):用于添加的已解析集合密钥。
MCP工具模式(请求/响应)
所有工具都接受JSON对象作为输入。响应是具有通用信封的JSON对象。下面的模式反映了当前的实现。
通用响应信封:
{
"ok": true,
"data": {},
"error": null
}
出现错误时:
{
"ok": false,
"data": null,
"error": {
"code": "string",
"message": "string",
"details": {}
}
}
共享架构片段:
{
"$defs": {
"creator": {
"type": "object",
"additionalProperties": false,
"properties": {
"creator_type": { "type": "string" },
"first_name": { "type": "string" },
"last_name": { "type": "string" },
"name": { "type": "string" }
},
"required": ["creator_type"],
"oneOf": [
{ "required": ["first_name", "last_name"] },
{ "required": ["name"] }
]
},
"tag": {
"type": "string",
"minLength": 1
}
}
}
zotero_search_items
请求架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"query": { "type": "string", "minLength": 1 },
"limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 },
"sort": { "type": "string", "default": "relevance" },
"start": { "type": "integer", "minimum": 0, "default": 0 },
"offset": { "type": "integer", "minimum": 0 },
"tags": {
"type": "array",
"items": { "$ref": "#/$defs/tag" },
"uniqueItems": true
}
},
"required": ["query"]
}
响应 data 架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"item_key": { "type": "string" },
"item_type": { "type": "string" },
"title": { "type": "string" },
"creators": { "type": "array", "items": { "$ref": "#/$defs/creator" } },
"date": { "type": "string" },
"doi": { "type": "string" },
"url": { "type": "string" },
"abstract": { "type": "string" },
"tags": { "type": "array", "items": { "$ref": "#/$defs/tag" } },
"extra": { "type": "string" },
"version": { "type": "integer" }
},
"required": ["item_key", "item_type", "title", "version"]
}
},
"total": { "type": "integer", "minimum": 0 },
"next_start": { "type": "integer", "minimum": 0 },
"sort_used": { "type": "string" }
},
"required": ["items", "total"]
}
zotero_get_item
请求架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"item_key": { "type": "string", "minLength": 1 }
},
"required": ["item_key"]
}
响应 data 架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"item": {
"type": "object",
"additionalProperties": false,
"properties": {
"item_key": { "type": "string" },
"item_type": { "type": "string" },
"title": { "type": "string" },
"creators": { "type": "array", "items": { "$ref": "#/$defs/creator" } },
"date": { "type": "string" },
"doi": { "type": "string" },
"url": { "type": "string" },
"abstract": { "type": "string" },
"tags": { "type": "array", "items": { "$ref": "#/$defs/tag" } },
"extra": { "type": "string" },
"version": { "type": "integer" },
"attachments": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"attachment_key": { "type": "string" },
"title": { "type": "string" },
"content_type": { "type": "string" },
"size": { "type": "integer" }
},
"required": ["attachment_key", "title"]
}
}
},
"required": ["item_key", "item_type", "title", "version"]
}
},
"required": ["item"]
}
zotero_get_sort_values
请求架构:
{
"type": "object",
"additionalProperties": false,
"properties": {}
}
响应 data 架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"values": { "type": "array", "items": { "type": "string" } },
"default": { "type": "string" },
"fallback": { "type": "string" }
},
"required": ["values", "default", "fallback"]
}
zotero_create_item
请求架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"item_type": { "type": "string", "minLength": 1 },
"title": { "type": "string", "minLength": 1 },
"creators": { "type": "array", "items": { "$ref": "#/$defs/creator" } },
"date": { "type": "string" },
"doi": { "type": "string" },
"url": { "type": "string" },
"abstract": { "type": "string" },
"tags": { "type": "array", "items": { "$ref": "#/$defs/tag" }, "uniqueItems": true },
"extra": { "type": "string" }
},
"required": ["item_type", "title"]
}
响应 data 架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"item_key": { "type": "string" },
"version": { "type": "integer" },
"item": { "type": "object" }
},
"required": ["item_key", "version"]
}
zotero_upload_attachment
上传必须提供以下内容之一 file_path, file_url,或 file_bytes_base64 (与 filename).本地文件必须可读,并且在配置的大小限制范围内。当 content_type 如果省略,服务器将从URL响应标头或文件扩展名推断它(回退 application/octet-stream).
请求架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"item_key": { "type": "string", "minLength": 1 },
"file_path": { "type": "string", "minLength": 1 },
"file_url": { "type": "string", "minLength": 1 },
"file_bytes_base64": { "type": "string", "minLength": 1 },
"filename": { "type": "string", "minLength": 1 },
"title": { "type": "string" },
"content_type": { "type": "string" }
},
"required": ["item_key"],
"anyOf": [
{ "required": ["item_key", "file_path"] },
{ "required": ["item_key", "file_url"] },
{ "required": ["item_key", "file_bytes_base64", "filename"] }
]
}
响应 data 架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"attachment_key": { "type": "string" },
"parent_item_key": { "type": "string" },
"title": { "type": "string" },
"content_type": { "type": "string" },
"size": { "type": "integer" },
"version": { "type": "integer" }
},
"required": ["attachment_key", "parent_item_key", "version"]
}
zotero_attach_arxiv_pdf
通过标识符或URL获取arXiv PDF,并将其作为附件上传。
请求架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"item_key": { "type": "string", "minLength": 1 },
"arxiv_id": { "type": "string", "minLength": 1 },
"title": { "type": "string" }
},
"required": ["item_key", "arxiv_id"]
}
响应 data 架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"attachment_key": { "type": "string" },
"parent_item_key": { "type": "string" },
"title": { "type": "string" },
"content_type": { "type": "string" },
"size": { "type": "integer" },
"version": { "type": "integer" },
"arxiv_id": { "type": "string" },
"pdf_url": { "type": "string" }
},
"required": ["attachment_key", "parent_item_key", "version", "arxiv_id", "pdf_url"]
}
zotero_list_collections
请求架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 },
"start": { "type": "integer", "minimum": 0, "default": 0 }
}
}
响应 data 架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"collections": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"collection_key": { "type": "string" },
"name": { "type": "string" },
"parent_key": { "type": "string" },
"version": { "type": "integer" },
"num_items": { "type": "integer" }
},
"required": ["collection_key", "name", "version"]
}
},
"total": { "type": "integer", "minimum": 0 },
"next_start": { "type": "integer", "minimum": 0 }
},
"required": ["collections", "total"]
}
zotero_add_item_to_collection
请求架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"item_key": { "type": "string", "minLength": 1 },
"collection_key": { "type": "string", "minLength": 1 },
"collection_name": { "type": "string", "minLength": 1 }
},
"required": ["item_key"]
}
响应 data 架构:
{
"type": "object",
"additionalProperties": false,
"properties": {
"item_key": { "type": "string" },
"collection_key": { "type": "string" }
},
"required": ["item_key", "collection_key"]
}
API映射(v1)
所有端点都是Zotero Web API v3,并使用 https://api.zotero.org 随着 Zotero-API-Version: 3.
zotero_search_items(已实施)->GET /users/{userID}/items- 查询参数:
q={query}(快速搜索),tag={tag}(可重复),limit={limit},sort={sort},start={start} zotero_list_collections(已实施)->GET /users/{userID}/collectionszotero_get_item(已实施)->GET /users/{userID}/items/{itemKey}zotero_create_item(已实施)->GET /items/new?itemType={item_type}(模板),然后POST /users/{userID}/items- Body:JSON数组,其中包含从工具输入填充的单个项对象
- 标题:
Content-Type: application/json,可选Zotero-Write-Token zotero_upload_attachment(已实现)->带子附件项的多步骤文件上传GET /items/new?itemType=attachment&linkMode=imported_file(附件模板)POST /users/{userID}/items随着parentItem={item_key}创建附件项POST /users/{userID}/items/{attachmentKey}/file(上传授权:md5、文件名、文件大小、mtime)POST {uploadUrl}(使用多部分上传prefix/suffix)那么POST /users/{userID}/items/{attachmentKey}/file注册上传zotero_attach_arxiv_pdf(已实现)->fetchhttps://arxiv.org/pdf/{arxivId}.pdf,则上传流程与zotero_upload_attachmentzotero_add_item_to_collection(已实施)->POST /users/{userID}/collections/{collectionKey}/items
安全模型
- API密钥仅通过环境变量提供。
- 没有API密钥写入磁盘。
- 日志必须避开敏感数据(令牌、附件内容)。
- 最小权限范围:仅限个人库。
- 应使用这些工具所需的最少权限创建API密钥(请参阅下面的“API密钥权限”)。
错误映射
所有工具都返回一致的错误包络:
{
"ok": false,
"data": null,
"error": {
"code": "ZOTERO_RATE_LIMITED",
"message": "Zotero rate limit exceeded.",
"details": {
"status": 429,
"retry_after": "120",
"request_id": "abc123",
"body": "{\"message\":\"Too Many Requests\"}"
}
}
}
Zotero HTTP错误被标准化为工具错误代码:
- 401/403 ->
ZOTERO_AUTH_ERROR - 404 ->
ZOTERO_NOT_FOUND - 429 ->
ZOTERO_RATE_LIMITED - 400/409/412/413/415/422 ->
ZOTERO_VALIDATION_ERROR - 5xx->
ZOTERO_UPSTREAM_ERROR - 其他意外反应->
ZOTERO_UPSTREAM_ERROR - 本地验证失败->
ZOTERO_VALIDATION_ERROR
错误 details 可能包括:
status:HTTP状态代码(如果可用)。body:Zotero提供的原始响应体(字符串)。retry_after:Retry-After标头值(如果存在)。request_id:X-Zotero-RequestID标头值(如果存在)。
速率限制和可靠性
对429/5xx和网络错误实施保守重试/回退。可选的内存读取缓存可用于GET请求。当Zotero回来时 Retry-After,客户端在重试之前等待该持续时间(秒或HTTP日期)。
日志记录
日志以JSON格式发送到stderr,并对敏感字段(令牌、文件路径、上传元数据)进行自动编辑。每次MCP工具调用都会得到一个 correlation_id 它包含在所有相关的日志行中,因此您可以端到端地跟踪单个请求。通过以下方式控制冗长 ZOTERO_MCP_LOG_LEVEL.使用 ZOTERO_MCP_DEBUG=1 以包括启动事件。
配置
此服务器仅从环境变量读取凭据。 .env 文件不会自动加载。
必修的:
ZOTERO_API_KEY:用于个人图书馆访问的Zotero API密钥。ZOTERO_USER_ID:与API密钥关联的Zotero用户ID。
API密钥权限
在Zotero帐户设置中创建Zotero API密钥,并仅授予此服务器所需的权限:
- 个人图书馆访问: 允许访问库 (所有读取操作都需要)。
- 写入权限: 允许写入访问 (需要
zotero_create_item,zotero_upload_attachment,zotero_attach_arxiv_pdf,以及zotero_add_item_to_collection). - 笔记访问: 允许笔记访问 不需要。
- 组访问权限:设置 所有组 到 无 (组库不在v1的范围内)。
如果您希望只读使用,请禁用写访问,但启用写的工具将返回身份验证错误。
快速入门(Docker)
包含Docker打包(请参阅 Dockerfile),但尚未提供已发布的图像。本地构建:
docker build -t zotero-mcp:local .
注意:一些沙盒环境(包括这个Codex沙盒)可能没有Docker守护进程访问权限,并且可能会因 permission denied while trying to connect to the docker API 错误。在这种情况下,直接在运行Docker Desktop的主机上运行构建。
然后用 stdio 这样地:
docker run --rm -i \
-e ZOTERO_API_KEY=your_key_here \
-e ZOTERO_USER_ID=your_user_id \
zotero-mcp:local
为了避免shell历史记录中的秘密,请使用env文件:
cat > .env.zotero-mcp <<'EOF'
ZOTERO_API_KEY=your_key_here
ZOTERO_USER_ID=your_user_id
EOF
docker run --rm -i --env-file .env.zotero-mcp \
zotero-mcp:local
地方发展(uv)
uv sync
uv run python -m zotero_mcp
关于依赖关系的注意事项:
- 开发/测试安装需要网络访问才能下载构建/运行时deps(例如,构建后端
hatchling). - 如果预先创建锁文件并缓存车轮,则可以进行离线安装。示例流程:(1)在有网络的机器上运行
uv lock和uv sync填充锁文件uv缓存,(2)副本uv.lock和那个uv脱机计算机的缓存目录(设置UV_CACHE_DIR到该缓存路径),(3)运行uv sync --frozen在离线机器上从缓存的轮子安装。
使用本地 .env 文件,复制示例并在运行前获取源代码:
cp .env.example .env.zotero-mcp
set -a
source .env.zotero-mcp
set +a
uv run python -m zotero_mcp
烟雾测试(服务器启动)
手动烟雾测试,以确认MCP stdio服务器启动并发出 server.start 日志事件。这不需要Zotero凭据,因为它不调用任何工具。
uv run python scripts/smoke_test.py
可选标志:
--timeout 10在慢速环境下等待更长时间。--verbose等待时打印服务器stderr。
MCP客户端配置(示例)
这是一个通用的MCP stdio 本地运行的配置示例。调整到宿主应用程序的配置格式。
{
"mcpServers": {
"zotero": {
"command": "uv",
"args": [
"run",
"python",
"-m",
"zotero_mcp"
],
"env": {
"ZOTERO_API_KEY": "your_key_here",
"ZOTERO_USER_ID": "your_user_id"
}
}
}
}
基于Docker的MCP客户端配置(替换镜像名称):
{
"mcpServers": {
"zotero": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"-e",
"ZOTERO_API_KEY=your_key_here",
"-e",
"ZOTERO_USER_ID=your_user_id",
"zotero-mcp:local"
}
}
}
}
环境变量
必修的:
ZOTERO_API_KEY:用于个人图书馆访问的Zotero API密钥。ZOTERO_USER_ID:与API密钥关联的Zotero用户ID。
可选:
ZOTERO_API_BASE:覆盖测试的基本URL。ZOTERO_MCP_DEBUG:设置为1以包括启动日志事件。ZOTERO_MCP_LOG_LEVEL:JSON日志的日志级别(默认INFO).ZOTERO_RETRY_MAX_ATTEMPTS:重试429/5xx/网络错误(默认值3).ZOTERO_RETRY_BASE_DELAY:基本退避延迟(秒)(默认值0.5).ZOTERO_RETRY_MAX_DELAY:最大退避延迟(秒)(默认值4.0).ZOTERO_READ_CACHE:设置为1启用内存中的GET缓存(默认关闭)。ZOTERO_READ_CACHE_TTL:缓存TTL(秒)(默认值30).ZOTERO_READ_CACHE_MAX:最大缓存条目数(默认值128).ZOTERO_UPLOAD_MAX_BYTES:最大上传大小(以字节为单位)(默认值52428800).
Docker MCP注册表对齐
Docker MCP注册表支持两种提交类型:Docker构建的映像和自制映像。一旦发布的图像可用,此项目计划遵循自行提供的图像路径。
登记处提交资产草案 registry/ (元数据、详细描述、使用示例)。在提交之前,用已发布的图像名称/标签更新占位符。
路线图
- 组库支持。
- 其他Zotero对象(集合、标签、注释)。
- 只读模式。
- 可配置的工具集。
发布检查表(v1)
- 确认README与当前工具表面和模式匹配。
- 运行单元测试以进行模式和验证。
- 使用模拟的Zotero响应运行集成测试。
- 构建Docker镜像,并通过MCP stdio在本地运行。
- 仅通过env变量验证身份验证;日志中没有凭据。
- 验证重试/回退、缓存和日志记录设置是否如文档所述。
- 确认docker运行和MCP客户端配置示例正常工作。
- 准备注册表提交资产(元数据、文档、图像)。
- 标签发布和发布。
贡献
看 CONTRIBUTING.md 为当地发展和贡献提供指导。