github 54yyyu/zotero-mcp v0.9.1

2 hours ago

Patch release. Four fixes, no interface change: the tool surface, the config schema and the CLI are identical to 0.9.0, so upgrading needs nothing from you.

uv tool upgrade zotero-mcp-server    # or: pip install -U zotero-mcp-server

Fixed

zotero_get_item_fulltext could return a trashed HTML snapshot instead of the paper (#427). In local and hybrid mode, LocalZoteroReader._iter_parent_attachments was the one item-level query that did not exclude deletedItems, and it had no ORDER BY. The shape this hits: you save a paper through the browser connector, Zotero indexes the connector's HTML snapshot and writes it a .zotero-ft-cache, you attach the real PDF and trash the snapshot as cleanup. From then on, whenever the PDF could not be parsed or resolved on disk, the fallback walked the attachments and returned the first cache it found, which was the publisher's landing page rather than the paper. Trashed attachments are now excluded and PDFs are yielded first. Reported, diagnosed and fixed by @feima3333.

Attachment uploads no longer take the fallback path on every attempt. The pyzotero floor moves from 1.8.0 to 1.13.3. Below that, step 0 of the upload handshake sent the caller-supplied path as the stored-file filename; 1.13.3 sends the basename instead (upstream urschrei/pyzotero#341). The Zotero API answers a path with 400 "Stored-file filename cannot contain a directory path", and pyzotero returned that payload under failure rather than raising, so every attach quietly re-ran through the two-step create-then-upload fallback added in #403. The fallback stays, since uploads fail for other reasons too, but it is no longer the path every upload takes. Raised by @feima3333.

chunking.enabled was accepted in silence on the OpenAI Batch API path (#416). That path builds its payload item-level and truncates each document at the embedding model's input limit, with no chunking step. So with openai_batch.enabled: true, turning chunking on changed nothing, and neither the CLI output, the logs, nor db-status said so. The reporter found it by diffing the generated batch-001-input.jsonl across two runs, byte-identical, and measured 30 of 58 documents truncated at the 8000-token cap. The combination now warns once at the start of the run, and db-status reports chunking as "requested but NOT applied" rather than echoing the setting back. Making the batch path chunk-aware is tracked separately. Reported by @dbuchber.

One hung PDF outline call could leave the whole server unresponsive (#431). All Zotero API access is serialized through a process-global lock, and zotero_get_pdf_outline held it across the attachment download and the PDF extraction subprocess, neither of which needs it. A single slow or stuck PDF therefore left every other tool timing out until the server was restarted. The lock is now taken around the Zotero API work alone. The extraction child also inherited the server's stdin, which under the stdio transport is the MCP pipe itself, so an orphaned child kept that pipe open; it now gets stdin=DEVNULL.

Concurrent tool calls could leave the server unable to write to stdout (#431). suppress_stdout swapped the process-global sys.stdout with no synchronization, so two threads interleaving their save and restore left the global pointing at a closed file. It is reference-counted under a lock now.

Known limitation

The Windows-specific hang reported in #431 is mitigated, not confirmed. The lock scope and the inherited stdin are fixed for certain, and the extraction subprocess now has a bounded timeout path plus Windows error-mode suppression. But why the subprocess outlived its own 30 second timeout is still open, and #431 stays open with it.

Don't miss a new zotero-mcp release

NewReleases is sending notifications on new releases.