Fixed
- Duplicate detection agrees with itself (#496). Duplicate grouping, the auto-merge DOI-conflict guard, the pre-add existence check and the semantic index's preprint filter now take their keys from
zotero_mcp.identifiers, so DOIs match in canonical form (10.1000/ABC,https://doi.org/10.1000/abc) and titles fold the way Zotero's own duplicate finder folds them. A DOI field holding a placeholder such asarticleno longer groups every item that carries it, and re-adding a paper whose stored DOI differs only in case no longer creates a second copy. - File uploads into a group library no longer go to your personal WebDAV (#591). Group libraries always store files in Zotero Storage, but with
ZOTERO_WEBDAV_*configured and writes going through the Web API,zotero_add_itemandzotero_attach_filesent the file only to WebDAV and left the group attachment without one. Other attach paths PUT a second copy to WebDAV, and if that failed they deleted the group attachment even though its file was already in Zotero Storage. The WebDAV steps now skip group libraries. - Automatic tags read as automatic in local mode (#620). Items read from
zotero.sqlitelisted every tag without its type, sozotero_get_item_metadata(format="json")and other reads showed automatic tags as manual, unlike the Zotero API. They now carry"type": 1. - Exported bibliographies are numbered 1..N (#619).
zotero_export_bibliographynumbered entries before dropping the ones that are empty once their HTML is stripped, so each dropped row used up a number and a 12-item collection came out as 4, 5, 7, 9, … Entries are now filtered first, then numbered. - PDFs with a scanner OCR text layer are no longer indexed metadata-only (#611). Files from Xerox or ABBYY scanners carry their text as an invisible layer over a page image, and pdf-inspector's markdown pass returned nothing for every page; Ghostscript and PDFCreator output could make it fail outright. Extraction now falls back to pdf-inspector's plain-text pass when the markdown pass yields no text or raises. That pass cannot be split by page, so the text is attributed to the first page and a
max_pagescap is applied as the same share of the text; an explicit page subset (as inzotero_read_pdf_pages) is unchanged. Reported by @timmyfaraday. - Percent-encoded doi.org URLs are read as the DOI they name (#630). A DOI URL copied from a browser's address bar, such as
https://doi.org/10.1002/%28SICI%291097-4636..., kept its%28/%3Cescapes throughnormalize_doi, so the item was saved with the encoded string in its DOI field and duplicate detection andif_existstreated it as different from the same DOI written plainly. A URL with an encoded%2Fwas not recognised as a DOI at all. The URL path is now percent-decoded; a bare DOI is left as written. - Merging duplicates no longer trashes distinct linked-file attachments (#633).
zotero_merge_duplicatesskips a duplicate's attachment when the keeper "already has" it, but the check compared only content type, filename, md5 and URL. Linked-file PDFs carry none of the last three, so any linked PDF on a duplicate matched any linked PDF on the keeper, stayed behind, and went to the Trash with the duplicate. Attachments are now compared by link mode and path as well, and one with no md5, path or URL is never treated as a duplicate. - Automatic tags stay automatic when tags are edited or items merged (#618).
add_tags/remove_tagsonzotero_update_itemandzotero_update_annotation, and the keeper's tags inzotero_merge_duplicates, were rebuilt from tag names alone, so automatic tags (e.g. MeSH headings from PubMed) became manual and "Delete Automatic Tags in This Library…" no longer reached them. Existing tags now keep their type, and tags copied from a duplicate keep the duplicate's. Reported by @rizakardas. - A read right after a write sees the write (#228). In local mode the database copy that reads use is refreshed at most every
ZOTERO_MCP_DB_SNAPSHOT_MIN_INTERVALseconds, and a write tool's own read just before writing usually used up that window, sozotero_get_item_metadataafterzotero_set_item_collections, orzotero_get_annotationsafterzotero_create_annotation, still showed the old state. After zotero-mcp writes, the next read takes one fresh copy regardless of that interval, then the interval applies again. - Switching to the personal library by the id
zotero_list_librariesshows now works (#603). In local mode the listing showed the personal library aslibraryID=1, butzotero_switch_libraryonly accepted0forlibrary_type='user', so the list-then-switch workflow the tool descriptions recommend always failed. Local mode now accepts0,user, or the personal library's listed libraryID (other ids are still rejected), and web mode mapsuser/0to the configuredZOTERO_LIBRARY_IDunlessZOTERO_LIBRARY_TYPEisgroup. Reported by @mronkko. - A failed re-embed no longer deletes an item's passages (#610). New passages are now written before the stale tail is pruned, so an embedding error no longer leaves the item unsearchable. A run with items still failing after the end-of-run retry no longer advances the sync watermark, and a process that fails to take the update lock no longer truncates the holder's PID.
- Changing an item's type keeps type-specific fields (#636).
zotero_update_item(fields={"item_type": ...})dropped every field the new type names differently, so turning a journal article into a conference paper lost itspublicationTitleinstead of moving it toproceedingsTitle, and the success report did not mention it. Values now carry over through their shared Zotero base field, as in the desktop client, and the success report lists what was carried over.