pydantic-zotero-mcp
pydantic-zotero-mcp
Ein MCP-Server, der KI-Agenten Lesezugriff auf eine Zotero-Bibliothek gewährt – Suche, Metadaten von Einträgen, Sammlungen, Tags, die eigenen Notizen des Forschers und den indizierten Volltext angehängter PDFs.
Siehe PRD.md für die Anforderungen.
Status: M1 (Lesen Kern) + M2 (Volltext) implementiert. Zitierformatierung und -export (M3), Prompts (M4) und Schreibwerkzeuge (M5) sind noch nicht gebaut – siehe Noch nicht implementiert.
Installation
Als Werkzeug (pipx)
Installiert den Befehl zotero-mcp in eine eigene isolierte Umgebung:
pipx install pydantic-zotero-mcp # or: pipx install /path/to/checkout
zotero-mcp --helpIn der Umgebung eines anderen Projekts
uv add pydantic-zotero-mcp # or: uv pip install pydantic-zotero-mcpFür die Entwicklung an diesem Server
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 checkRelated MCP server: zotero-cli-cc
Konfiguration
Holen Sie sich einen schreibgeschützten API-Schlüssel und Ihre numerische Benutzer-ID von https://www.zotero.org/settings/keys. Die Bibliotheks-ID ist die Nummer, nicht Ihr Benutzername.
export ZOTERO_API_KEY=...
export ZOTERO_LIBRARY_ID=123456 # numeric
export ZOTERO_LIBRARY_TYPE=user # or groupVariable | Standard | Zweck |
| — | Web-API-Schlüssel (erforderlich, außer |
| — | Numerische Benutzer- oder Gruppen-ID |
|
|
|
|
| Stattdessen die Zotero-7-Desktop-API lesen: kein Schlüssel, kein Rate Limit, schreibgeschützt |
|
| Für M5 reserviert; es gibt noch keine Schreibwerkzeuge |
|
| Standard-Obergrenze für Volltext; |
|
| Für M3 reserviert |
|
| Obergrenze für Upstream-Anfragen (Zotero bittet um ≤ 4) |
|
|
|
|
| HTTP-Bindeadresse |
|
| HTTP-Port |
|
| HTTP-Mount-Pfad |
| — | Bearer-Token; für HTTP erforderlich |
CLI-Flags überschreiben Umgebungsvariablen.
Ausführen
Nach der Installation ist zotero-mcp der Einstiegspunkt – kein Interpreterpfad, kein python -m, kein Arbeitsverzeichnis, das stimmen muss, was ein MCP-Client mit command: erwartet:
# 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 --localAus einem Checkout heraus funktioniert ohne Installation weiterhin python -m zotero_mcp:
uv run python -m zotero_mcpDer Start mit --transport http und ohne Token beendet sich mit Exit-Code 2, anstatt unauthentifiziert zu dienen: Dies ist ein Lesekanal in eine persönliche Bibliothek.
Im Speicher (eingebettet in einen Agentenprozess)
Kein Subprozess, kein Socket. Die Einstellungen werden injiziert, sodass der Host nie Umgebungsvariablen benötigt:
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 modelDas Importieren von zotero_mcp hat keine Seiteneffekte – keine Konfiguration wird gelesen, kein Client erstellt, kein Netzwerk – genau das macht das Einbetten möglich. Es gibt einen Test, der das durchsetzt.
Erkennung über Einstiegspunkt
Für Hostanwendungen, die gebündelte MCP-Server über Python-Einstiegspunkte erkennen, deklariert dieses Paket einen in der Gruppe deep_research.mcp_servers:
[project.entry-points."deep_research.mcp_servers"]
zotero = "zotero_mcp:build_server"build_server() akzeptiert keine Argumente und leitet die Einstellungen aus der Umgebung ab – installieren Sie dieses Paket in der Umgebung des Hosts, und der Host kann den Server unter dem Namen zotero im Prozess auflösen und ausführen, ohne etwas per Pfad aus einer Konfigurationsdatei zu importieren.
Ein Hinweis zur Abstimmung für automatisierte Hosts: Die Standard-Obergrenze für Volltext dieses Servers beträgt 100.000 Zeichen (~25.000–30.000 Token für einen einzelnen get_item_fulltext-Aufruf), was für die interaktive Nutzung großzügig ist und viel zu groß für einen Agenten, der viele Aufrufe innerhalb eines Token-Budgets tätigt – übergeben Sie pro Aufruf ein kleineres max_chars oder senken Sie ZOTERO_FULLTEXT_MAX_CHARS.
Werkzeuge
Werkzeug | Zweck |
| Größe, Modus, Berechtigungen. Günstiger Orientierungsaufruf – zuerst nutzen |
| Primärer Einstiegspunkt. |
| Kürzlich hinzugefügte Einträge, neueste zuerst |
| „Habe ich das schon?" per DOI, ISBN, arXiv-ID oder Schlüssel |
| Vollständige Metadaten; |
| Anhänge und Notizen, mit |
| Die eigenen Notizen des Forschers, HTML entfernt |
| Indizierter Anhangstext; löst Eltern → Anhang auf |
| Verschachtelter Sammlungsbaum |
| Einträge in einer Sammlung |
| Tag-Vokabular, optional präfixgefiltert |
Ressourcen: 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.
Designhinweise
Projektion ist der Punkt. Rohes Zotero-JSON ist ~1 KB pro Eintrag an links, library, meta und leeren Typfeldern. zotero_mcp/projection.py reduziert eine Seite mit 25 Einträgen von ~6.100 auf ~2.400 geschätzte Token (39 % des Rohwerts), unter dem PRD-Budget von 4.000. Null-Felder werden bei der Serialisierung von CompactModel entfernt.
pyzotero ist synchron und zustandsbehaftet. Zotero.request und Zotero.links werden bei jedem Aufruf überschrieben, und Total-Results wird danach von der Instanz gelesen – ein gemeinsam genutzter Client würde also die Summen eines anderen Aufrufs melden. gateway.py hält einen Pool von bis zu ZOTERO_MAX_CONCURRENCY Clients, leiht sich pro Vorgang einen aus und liest die Antwortmetadaten im selben Worker-Thread, der den Client hält. Jeder Aufruf läuft über anyio.to_thread.run_sync, sodass die Ereignisschleife nie blockiert wird.
Backoff ist pyzoteros Aufgabe. pyzotero ≥ 1.13 beachtet bereits Backoff / Retry-After und wiederholt 429 intern, daher implementiert das Gateway es nicht neu. Es fügt nur einen begrenzten 3-Versuche-Wiederholungsversuch für vorübergehende Transport- und 5xx-Fehler hinzu.
Nichts wird stillschweigend abgeschnitten. Suchen melden total_matched, truncated und next_start; Volltext meldet total_chars und truncated.
Ergebnisse sind Kandidaten, keine Urteile (PRD D3). find_item_by_identifier gibt matched_on (key / doi / title / identifier / none) sowie ein Konfidenzniveau und alle plausiblen Kandidaten zurück – ein Preprint und seine veröffentlichte Version überleben beide. Der Aufrufer filtert.
Abweichungen vom PRD
Wissenswert, da jede eine Ermessensentscheidung während der Implementierung war:
Kein
mcp-Objekt auf Modulebene. PRD 7.2 forderte sowohl einmcp = create_server()auf Modulebene als auch keine Seiteneffekte beim Import. Das widerspricht sich: Das Erstellen des Servers validiert die Konfiguration, daher würde eine Instanz auf Modulebene auf jedem Rechner ohne Zotero-Umgebungsvariablen einenImportErrorauslösen und den In-Memory-Pfad brechen, den sie unterstützen sollte. Es existieren nurcreate_server()/build_default_server().Schreibwerkzeuge werden bedingt registriert, nicht mit
enabled=False. PRD 5.5 spezifizierte@mcp.tool(enabled=False), aber FastMCP 3.x hat keinenabled-Kwarg, und ein deaktiviertes, aber aufgelistetes Werkzeug kostet trotzdem Kontext. Wenn M5 eintrifft, werden Schreibwerkzeuge einfach nicht registriert, sofern nichtZOTERO_ALLOW_WRITES=truegesetzt ist.Dieser Server zielt auf FastMCP 3.x. Zwei 3.x-Besonderheiten prägen den Code hier:
enabledist aus den Dekorateuren entfernt, undresult.dataist ein generiertes Pydantic-Modell, währendresult.structured_contentdas einfache Dict ist – die Tests prüfen auf Letzteres, was auch die Null-Auslassung auf der Leitung verifiziert.has_fulltextist dreiwertig. PRD 6 typisierte es alsbool, aber die Bestimmung für ein übergeordnetes Element erfordert eine separate Kinderanfrage pro Element, was eine Suche mit 25 Einträgen zu 26 Anfragen machen würde. Es istFalse, wenn ein Element überhaupt keine Kinder hat,True/Falsefür Anhänge und nachget_item(include_children=True)undnull(weggelassen), wenn unbestimmt.ItemSummary.num_childrenliefert das günstige Signal.find_item_by_identifiergibtCitationMatchzurück, nichtItemSummary | None. Folgt aus D3 – die alte Signatur traf genau den Identitätsaufruf, den diese Entscheidung auf den Client verlagert hat.matched_onerhieltkeyundidentifierzusätzlich zu den vier PRD-Werten, um einen exakten Schlüsseltreffer von einem schwachen Suchergebnis zu unterscheiden.list_recent_items(since_days=...)filtert lokal. Zotero hat keinen serverseitigen Datumsfilter, daher kann ein enges Zeitfenster weniger Elemente alslimitzurückgeben; derhintin der Antwort sagt, wann das passiert ist.
Tests
uv run pytest # 80 passedDie Suite verwendet den In-Memory-Transport von FastMCP gegen ein FakeZotero, das das Verhalten von pyzotero nachbildet, Metadaten von der Instanz zu lesen. Kein Netzwerk, kein Subprozess, keine echten Anmeldedaten. Abdeckung: Schemaoberfläche, Projektion und Token-Budget, Paginierung und Abschneideberichte, Volltext-Obergrenze und Elternauflösung, Trefferabruf (Preprint/veröffentlichte Paare werden beide zurückgegeben), Fehlermeldungsqualität, Ressourcenvorlagenvalidierung einschließlich Traversierungsversuchen, Konfigurationsvalidierung, CLI-Vorrang, Gateway-Wiederholung/-Zwischenspeicherung und ein Import-Reinheitscheck, der fehlschlägt, wenn das Importieren des Pakets das Netzwerk berührt.
Noch nicht implementiert
M3 –
format_citation,format_bibliography,export_itemsM4 – die vier Prompts (
literature_review,find_related_work,check_citations,summarize_reading), Logfire-InstrumentierungM5 – Schreibwerkzeuge (
create_item,update_item_fields,add_item_tags,add_items_to_collection,create_note) mit versionsgeprüfter PATCH-Semantik. Das Löschen ist dauerhaft außerhalb des Rahmens.
This server cannot be installed
Maintenance
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
- AlicenseAqualityCmaintenanceA 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.815MIT
- AlicenseNot gradedqualityAmaintenanceMCP 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.198AGPL 3.0
- AlicenseNot gradedqualityAmaintenanceMCP server that lets AI assistants search, create, organize, and cite from a Zotero library.3MIT
- AlicenseNot gradedqualityDmaintenanceMCP server that connects AI assistants to your Zotero library, enabling full-text PDF extraction and metadata search.MIT
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
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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