Upgrading
This release changes tool names. Fifteen tools were merged into six, so saved prompts, permission allowlists, or scripts naming the old tools need updating. No back-compat aliases ship, deliberately: an alias re-sends its schema on every request, which is exactly the cost this change exists to remove.
Some tools are also now opt-in and absent unless enabled. If you rely on Scite, duplicate detection, feeds, related-items links, or the corpus-level discovery tools, set ZOTERO_MCP_TOOLSETS — see below.
| Was | Now |
|---|---|
zotero_add_by_doi, add_by_url, add_by_isbn, add_by_bibtex, add_by_csl_json, add_from_file
| zotero_add_item(source=…, source_type="auto")
|
zotero_batch_update_tags, zotero_batch_update_extra
| zotero_batch_update
|
zotero_get_item_children, zotero_get_items_children
| zotero_get_item_children (one key or many)
|
zotero_create_annotation, zotero_create_area_annotation
| zotero_create_annotation (rect= selects area mode)
|
zotero_create_note, zotero_update_note, zotero_delete_note
| zotero_manage_note(action=…)
|
zotero_search_notes
| zotero_get_notes(query=…)
|
zotero_manage_collections
| zotero_set_item_collections
|
A smaller tool surface
Every registered tool is sent to the model on every request, so the tool list is a fixed tax on your context window before you type anything. It was 62 tools / ~22.9k tokens.
| tools | tokens | |
|---|---|---|
| 0.8.0 | 62 | 22,932 |
0.9.0, full surface (ZOTERO_MCP_TOOLSETS=all)
| 49 | 17,592 |
| 0.9.0, default | 37 | 13,765 |
0.9.0, core only (=none)
| 31 | 11,958 |
Token weight was the smaller problem. Near-duplicate names were a selection hazard: choosing between six add_by_* variants, or between get_item_children and get_items_children, is a coin flip that costs a full round trip when the model gets it wrong.
zotero_update_item alone dropped from 1,283 to 677 tokens by taking a fields mapping instead of 21 flat Zotero field names in its signature. fields accepts any Zotero API field plus creators, so it reaches strictly more than the old parameters did.
Optional tool groups: ZOTERO_MCP_TOOLSETS
| Value | Effect |
|---|---|
| (unset) | Default profile |
all
| Everything — the 0.8.0 behaviour |
none
| Core tools only |
scite,feeds
| Core plus named groups |
all,-scite
| Everything except named groups |
Groups: scite, duplicates, discovery, feeds, relations, libraries, search-admin, pdf-geometry, chatgpt-connector. Anything not in a group is core and always present. An unknown group name fails at startup rather than silently serving a surface you didn't ask for.
The ChatGPT connector's search/fetch pair is now scoped by transport — on for streamable-http/sse, off for stdio — so stdio users stop paying ~690 tokens for two tools ChatGPT alone can reach, and a bare search stops competing with the Zotero search tools.
Fixes
Ollama indexing ignored embedding_config.timeout, so update-db could report success having written nothing (#423). The ollama branch built its embedding function without the configured timeout, so every request used the 120s default. With chunking enabled each /api/embed call carries a whole item batch worth of chunks, which on modest hardware runs well past 120s — every batch timed out, was queued for retry, and the progress bar advanced to completion over an empty database. Reported with a diagnosis and fix by @physicien, who measured a ~5,600-item library indexing to 163,091 embeddings in 9h34m with 0 errors once the timeout was honored.
Ollama requests are now also split into request_batch_size windows (default 64), matching what the OpenAI embedding function has always done, so a timeout bounds one window rather than an entire GPU pass. A response with fewer vectors than inputs is now an error instead of silently shifting every later document onto the wrong embedding.
Bibliography and citation rendering works in local-only mode with no API credentials (#371). The previous fix concluded the local API had no citation engine and routed rendering through the web API, locking local-only users out. The real constraint is narrower: content=bib/citation/bibtex implies format=atom, and it is Atom the local API rejects. Asking the JSON way — include=bib/citation with style, or format=bibtex — is served locally with no credentials at all.
Two further corrections found while verifying against a real library: a library-wide export now draws from top-level items, since attachments and notes rendered as blanks; and item_keys selections are filtered client-side, because the local API answers an itemKey filter with the requested items plus unrelated ones and would otherwise have exported the wrong bibliography.
Tests run from a git worktree exercised the wrong source tree. With the package installed editable against the main checkout, a suite run from a worktree silently imported that checkout instead, reporting green regardless of the edits under review.
Full changelog: https://github.com/54yyyu/zotero-mcp/blob/main/CHANGELOG.md