Skip to main content
Glama
jmlon

pydantic-zotero-mcp

by jmlon

pydantic-zotero-mcp

MCP-сервер, который предоставляет ИИ-агентам доступ на чтение к библиотеке Zotero — поиск, метаданные элементов, коллекции, теги, собственные заметки исследователя и индексированный полный текст прикреплённых PDF-файлов.

Требования см. в PRD.md.

Статус: M1 (чтение ядра) + M2 (полный текст) реализованы. Форматирование цитат и экспорт (M3), промпты (M4) и инструменты записи (M5) ещё не созданы — см. Не реализовано.

Установка

Как инструмент (pipx)

Устанавливает команду zotero-mcp в собственное изолированное окружение:

pipx install pydantic-zotero-mcp        # or: pipx install /path/to/checkout
zotero-mcp --help

В окружение другого проекта

uv add pydantic-zotero-mcp              # or: uv pip install pydantic-zotero-mcp

Для разработки этого сервера

git clone https://github.com/jmlon/pydantic-zotero-mcp
cd pydantic-zotero-mcp
uv sync           # creates ./.venv from this project's own lock file
uv run pytest
uv run ruff check

Related MCP server: zotero-cli-cc

Настройка

Получите ключ API только для чтения и ваш числовой идентификатор пользователя на https://www.zotero.org/settings/keys. Идентификатор библиотеки — это число, а не ваше имя пользователя.

export ZOTERO_API_KEY=...
export ZOTERO_LIBRARY_ID=123456      # numeric
export ZOTERO_LIBRARY_TYPE=user      # or group

Переменная

По умолчанию

Назначение

ZOTERO_API_KEY

Ключ веб-API (обязателен, если не ZOTERO_LOCAL=true)

ZOTERO_LIBRARY_ID

Числовой идентификатор пользователя или группы

ZOTERO_LIBRARY_TYPE

user

user или group

ZOTERO_LOCAL

false

Чтение через локальный API Zotero 7: без ключа, без лимитов, только чтение

ZOTERO_ALLOW_WRITES

false

Зарезервировано для M5; инструментов записи пока нет

ZOTERO_FULLTEXT_MAX_CHARS

100000

Потолок полного текста по умолчанию; max_chars в вызове переопределяет его

ZOTERO_DEFAULT_STYLE

chicago-note-bibliography

Зарезервировано для M3

ZOTERO_MAX_CONCURRENCY

4

Лимит одновременных запросов (Zotero просит ≤ 4)

ZOTERO_MCP_TRANSPORT

stdio

stdio или http

ZOTERO_MCP_HOST

127.0.0.1

Адрес привязки HTTP

ZOTERO_MCP_PORT

8000

HTTP-порт

ZOTERO_MCP_PATH

/mcp

Путь монтирования HTTP

ZOTERO_MCP_AUTH_TOKEN

Bearer-токен; обязателен для HTTP

Флаги CLI переопределяют переменные окружения.

Запуск

После установки zotero-mcp — это точка входа: не нужен путь к интерпретатору, не нужен python -m, не нужно угадывать рабочую директорию — именно то, что нужно клиенту MCP в command::

# stdio (default) — an agent launches this as a subprocess
zotero-mcp

# streamable HTTP — requires ZOTERO_MCP_AUTH_TOKEN
ZOTERO_MCP_AUTH_TOKEN=secret zotero-mcp --transport http --port 8000

# read the Zotero desktop app instead of the web API
zotero-mcp --local

Из клонированного репозитория, без установки, по-прежнему работает python -m zotero_mcp:

uv run python -m zotero_mcp

Запуск с --transport http без токена завершается с кодом 2, а не обслуживает неаутентифицированные запросы: это канал чтения в личную библиотеку.

В памяти (встроено в процесс агента)

Никаких подпроцессов и сокетов. Настройки внедряются, поэтому хосту не нужны переменные окружения:

from fastmcp import Client
from zotero_mcp import ZoteroSettings, create_server

server = create_server(
    ZoteroSettings(
        api_key=key,
        library_id="123456",
        library_type="user",
    )
)

async with Client(server) as client:  # lifespan opens here
    result = await client.call_tool("search_items", {"query": "attention"})
    print(result.structured_content["items"])  # dict; result.data is a model

Импорт zotero_mcp не имеет побочных эффектов — ни чтения конфигурации, ни создания клиента, ни сетевых операций — именно это делает встраивание возможным. Есть тест, который это проверяет.

Обнаружение через точку входа

Для хост-приложений, которые обнаруживают встроенные MCP-серверы через точки входа Python, этот пакет объявляет одну в группе deep_research.mcp_servers:

[project.entry-points."deep_research.mcp_servers"]
zotero = "zotero_mcp:build_server"

build_server() не принимает аргументов и получает настройки из окружения — установите этот пакет в окружение хоста, и хост сможет найти и запустить сервер в процессе по имени zotero, без импорта по пути из конфигурационного файла.

Одно замечание по настройке для автоматизированных хостов: потолок полного текста по умолчанию у этого сервера — 100 000 символов (~25–30 тыс. токенов для одного вызова get_item_fulltext), что щедро для интерактивного использования и слишком много для агента, делающего множество вызовов в рамках токенного бюджета — передавайте меньший max_chars в каждом вызове или снизьте ZOTERO_FULLTEXT_MAX_CHARS.

Инструменты

Инструмент

Назначение

get_library_info

Размер, режим, права. Дешёвый ориентировочный вызов — используйте первым

search_items

Основная точка входа. mode="metadata" или "fulltext" (поиск по тексту PDF)

list_recent_items

Недавно добавленные элементы, сначала новые

find_item_by_identifier

«Есть ли у меня уже это?» по DOI, ISBN, arXiv ID или ключу

get_item

Полные метаданные; include_children=True также перечисляет вложения и заметки

get_item_children

Вложения и заметки, с may_have_fulltext для каждого вложения

get_item_notes

Собственные заметки исследователя, HTML удалён

get_item_fulltext

Индексированный текст вложений; разрешение родитель → вложение

list_collections

Вложенное дерево коллекций

list_collection_items

Элементы в одной коллекции

list_tags

Словарь тегов, опционально с фильтром по префиксу

Ресурсы: zotero://library/info, zotero://collections, zotero://items/{key}, zotero://items/{key}/fulltext, zotero://collections/{key}/items, zotero://schema/item-types, zotero://schema/item-types/{type}/fields.

Заметки по дизайну

Проекция — это суть. Сырой JSON Zotero — это ~1 КБ на элемент из links, library, meta и пустых полей типов. zotero_mcp/projection.py сокращает страницу из 25 элементов с ~6 100 до ~2 400 оценочных токенов (39% от сырого), в пределах бюджета PRD в 4 000. Поля со значением null отбрасываются при сериализации через CompactModel.

pyzotero синхронен и хранит состояние. Zotero.request и Zotero.links перезаписываются каждым вызовом, а Total-Results считывается с экземпляра после — поэтому общий клиент, используемый конкурентно, сообщил бы итоги другого вызова. gateway.py хранит пул из до ZOTERO_MAX_CONCURRENCY клиентов, выдаёт по одному на операцию и читает метаданные ответа в том же рабочем потоке, который удерживает клиент. Каждый вызов проходит через anyio.to_thread.run_sync, поэтому цикл событий никогда не блокируется.

Backoff — забота pyzotero. pyzotero ≥ 1.13 уже учитывает Backoff / Retry-After и повторяет 429 внутри, поэтому шлюз не реализует это заново. Он добавляет ограниченные 3 попытки повтора только для транзиентных транспортных ошибок и ошибок 5xx.

Ничего не обрезается молча. Поиски сообщают total_matched, truncated и next_start; полный текст сообщает total_chars и truncated.

Результаты — это кандидаты, а не вердикты (PRD D3). find_item_by_identifier возвращает matched_on (key / doi / title / identifier / none), а также уровень уверенности и все правдоподобные кандидаты — препринт и его опубликованная версия сохраняются оба. Фильтрует вызывающая сторона.

Отклонения от PRD

О них стоит знать, поскольку каждое было осознанным решением в ходе реализации:

  1. Нет модульного объекта mcp. PRD 7.2 требовал и модульный mcp = create_server(), и отсутствие побочных эффектов при импорте. Это противоречит друг другу: создание сервера проверяет конфигурацию, поэтому модульный экземпляр вызывает ImportError на любой машине без переменных окружения Zotero и ломает путь встраивания в память, который он должен был поддерживать. Существуют только create_server() / build_default_server().

  2. Инструменты записи будут регистрироваться условно, а не с enabled=False. PRD 5.5 указывал @mcp.tool(enabled=False), но в FastMCP 3.x нет аргумента enabled, а отключённый, но перечисленный инструмент всё равно занимает контекст. Когда выйдет M5, инструменты записи просто не будут регистрироваться, если только ZOTERO_ALLOW_WRITES=true.

    Этот сервер нацелен на FastMCP 3.x. Две особенности 3.x формируют код здесь: enabled отсутствует в декораторах, а result.data — это сгенерированная модель pydantic, тогда как result.structured_content — это обычный словарь; тесты проверяют второе, что также проверяет пропуск null-значений на проводе.

  3. has_fulltext трёхзначный. PRD 6 типизировал его как bool, но определение его для родительского элемента требует отдельного запроса детей для каждого элемента, что сделало бы поиск из 25 элементов 26 запросами. Он равен False, когда у элемента вообще нет детей, True/False для вложений и после get_item(include_children=True), и null (опущен), когда не определён. ItemSummary.num_children даёт дешёвый сигнал.

  4. find_item_by_identifier возвращает CitationMatch, а не ItemSummary | None. Это следует из D3 — старая сигнатура делала ровно тот запрос на идентичность, который это решение перенесло на клиента.

  5. matched_on получил key и identifier сверх четырёх значений из PRD, чтобы отличать точное совпадение по ключу от слабого совпадения по поиску.

  6. list_recent_items(since_days=...) фильтрует локально. У Zotero нет серверного фильтра по дате, поэтому узкое окно может вернуть меньше элементов, чем limit; в ответе hint сообщает, когда это произошло.

Тесты

uv run pytest      # 80 passed

Набор использует внутрипроцессный транспорт FastMCP с FakeZotero, который воспроизводит поведение pyzotero по чтению метаданных с экземпляра. Без сети, без подпроцессов, без реальных учётных данных. Покрытие: поверхность схемы, проекция и токенный бюджет, пагинация и отчётность об обрезке, потолок полного текста и разрешение родителя, полнота совпадений (пары препринт/опубликованная версия возвращаются обе), качество сообщений об ошибках, проверка шаблонов ресурсов, включая попытки обхода, проверка конфигурации, приоритет CLI, повторные попытки/кэширование шлюза и проверка чистоты импорта, которая падает, если импорт пакета касается сети.

Не реализовано

  • M3format_citation, format_bibliography, export_items

  • M4 — четыре промпта (literature_review, find_related_work, check_citations, summarize_reading), инструментирование Logfire

  • M5 — инструменты записи (create_item, update_item_fields, add_item_tags, add_items_to_collection, create_note) с семантикой PATCH с проверкой версий. Удаление навсегда вне области действия.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    A lightweight MCP server that connects AI agents to a local Zotero library for paper management and metadata retrieval. It enables users to search titles and abstracts, browse collections, and automatically ingest papers via arXiv ID or DOI with PDF attachments.
    8
    15
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server that exposes 45 tools for Zotero reference management, enabling AI agents to read/write items, search, extract PDF text, and manage workspaces via the Zotero CLI.
    198
    AGPL 3.0

View all related MCP servers

Related MCP Connectors

  • Remote MCP server for full read/write access to a Zotero library

  • Agentic search over your Dewey document collections from any MCP-compatible client.

  • An MCP server that gives your AI access to the source code and docs of all public github repos

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jmlon/pydantic-zotero-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server