Zotero Native MCP
NPM · ZOTERO-NATIVE-MCP · SCANNED SEP 21
Read and write your local Zotero 10 library. No plugin, no zotero.org API key, no cloud.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security98
- No malware found by supply-chain analysis.Pass
- No known CVEs affecting this package version or its production dependencies.Pass
- No install/post-install scripts declared.Pass
- 31 of 96 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency97
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Cryptographically verified build provenance (signed, bound to dvdsosa/zotero-native-mcp). View diagnostics → Pass
- Clear OSI-approved license (MIT).Pass
- Actively maintained (last published 17 days ago).Pass
- Disclosure check failed: no security disclosure policy was found in the source repository. See how to fix → Fail
Schema Quality & AI Usability71
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 5122 tokens (~182/item across 28 items; 28 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management60
- Stability observed for 18 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage100
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 100% of tool parameters carry a description.Pass
- Structured output schemas are declared (100% of tools); any adoption earns full credit.Pass
Tool Safety94
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- 3 of 4 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "zotero_run_saved_search" implies "execute" and declares readOnlyHint instead, contradicting what its own name says it does. See how to fix → Partial
- An AI judge read all 29 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
How do I install the Zotero Native MCP server?
Zotero Native MCP runs locally as an npm package, launched with npx -y zotero-native-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
npm · zotero-native-mcp
claude mcp add dvdsosa-zotero-native-mcp -- npx -y zotero-native-mcp
{
"mcpServers": {
"dvdsosa-zotero-native-mcp": {
"command": "npx",
"args": [
"-y",
"zotero-native-mcp"
]
}
}
} {
"servers": {
"dvdsosa-zotero-native-mcp": {
"command": "npx",
"args": [
"-y",
"zotero-native-mcp"
]
}
}
} codex mcp add dvdsosa-zotero-native-mcp -- npx -y zotero-native-mcp
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"dvdsosa-zotero-native-mcp": {
"type": "local",
"command": [
"npx",
"-y",
"zotero-native-mcp"
],
"enabled": true
}
}
} openclaw mcp add dvdsosa-zotero-native-mcp --command npx --arg -y --arg zotero-native-mcp
mcp_servers:
dvdsosa-zotero-native-mcp:
command: "npx"
args: ["-y", "zotero-native-mcp"] {
"McpServers": {
"dvdsosa-zotero-native-mcp": {
"Transport": "stdio",
"Command": "npx",
"Arguments": [
"-y",
"zotero-native-mcp"
]
}
}
} assistant mcp add dvdsosa-zotero-native-mcp -t stdio -c npx -a -y zotero-native-mcp
{
"mcpServers": {
"dvdsosa-zotero-native-mcp": {
"command": "npx",
"args": [
"-y",
"zotero-native-mcp"
]
}
}
} Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 21 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 57 to 60. That category is still filling its 30-day observation window: 17 days of observed history at the previous scan, 18 at this one. The score rises as the window fills, whether or not the server changes.
- 19 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 50 to 53. That category is still filling its 30-day observation window: 15 days of observed history at the previous scan, 16 at this one. The score rises as the window fills, whether or not the server changes.
- 17 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 43 to 47. That category is still filling its 30-day observation window: 13 days of observed history at the previous scan, 14 at this one. The score rises as the window fills, whether or not the server changes.
- 15 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 37 to 40. That category is still filling its 30-day observation window: 11 days of observed history at the previous scan, 12 at this one. The score rises as the window fills, whether or not the server changes.
- 13 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 30 to 33. That category is still filling its 30-day observation window: 9 days of observed history at the previous scan, 10 at this one. The score rises as the window fills, whether or not the server changes.
- 11 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 23 to 27. That category is still filling its 30-day observation window: 7 days of observed history at the previous scan, 8 at this one. The score rises as the window fills, whether or not the server changes.
- 8 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 13 to 17. That category is still filling its 30-day observation window: 4 days of observed history at the previous scan, 5 at this one. The score rises as the window fills, whether or not the server changes.
- 6 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 7 to 10. That category is still filling its 30-day observation window: 2 days of observed history at the previous scan, 3 at this one. The score rises as the window fills, whether or not the server changes.
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 21 Sept 2026 · Analysed npm/zotero-native-mcp@1.0.1
Provenance Verified
A signed build attestation was found and verified, binding this exact artifact to the source repository it claims to come from.
| Result | Verified |
|---|---|
| Ecosystem | npm |
| Reason | Verified |
| Discovered via | Registry attestation endpoint |
| Source repo | dvdsosa/zotero-native-mcp |
| Certificate issuer | https://token.actions.githubusercontent.com |
| Certificate SAN | https://github.com/dvdsosa/zotero-native-mcp/.github/workflows/publish.yml@refs/heads/main |
| Rekor log index | 2704769964 |
| Predicate type | https://slsa.dev/provenance/v1 |
| Subject digest | sha512:e0e2692ab1518809b1621b611ec217720413d626b6bff3ca4ff3fd1c44aab2968cb9b7006c5bbe40834a6ca17f27627939772e4b0b37c7706537d1f8f |
Background: How many MCP packages publish verified provenance →
Dependencies 96 packages
| Packages resolved | 96 |
|---|---|
| Stale | 31 |
| Tree resolution | Complete |
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
zotero_add_items_to_collection Add items to a collection ~125
File existing items into a collection, keeping every collection they already belong to. An item in Zotero can sit in any number of collections, so this adds rather than moves. Items already in the collection are reported as unchanged and cost no write.
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKey | string | yes | Target collection key. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| itemKeys | array | yes | Keys of the items to file. |
| Name | Type | Req | Description |
|---|---|---|---|
| added | array | yes | – |
| alreadyPresent | array | yes | – |
| collectionKey | string | yes | – |
No examples provided.
zotero_attach_file Attach a local file ~320
Attach a file from disk to a Zotero item, or add it as a standalone attachment. mode="linked" (default) records the path only: instant for any file size, but the file must stay put and the attachment does not sync to zotero.org. mode="imported" copies the file into Zotero's storage, so it syncs and survives the original being moved. Group libraries accept only "imported", since a local path means nothing to other members. filePath must be absolute. Pass parentItemKey to hang the file off an existing reference; omit it for a standalone attachment, optionally filed into collections.
| Name | Type | Req | Description |
|---|---|---|---|
| collections | array | – | Collections to file the attachment into. Only valid for standalone attachments (no parentItemKey). |
| filePath | string | yes | Absolute path to the file on this machine, e.g. "/Users/me/papers/smith-2024.pdf". |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| mode | string | – | "linked" stores the path only (instant, no copy, no sync); "imported" copies the file into Zotero (syncs). |
| parentItemKey | string | – | Item this file belongs to. Omit to create a standalone attachment item. |
| tags | array | – | Tags to attach. |
| title | string | – | Attachment title shown in Zotero. Defaults to the file name. |
| Name | Type | Req | Description |
|---|---|---|---|
| attachment | object | yes | – |
| attachmentKey | string | yes | – |
| bytes | number | yes | – |
| contentType | string | yes | – |
| filePath | string | yes | – |
| mode | string | yes | – |
| uploaded | boolean | yes | True when file bytes were transferred; false for linked files and deduplicated uploads. |
No examples provided.
zotero_authorize Request Zotero write access ~191
Request a local API key so write tools (creating collections, items, and attachments) can run. This raises a modal dialog inside Zotero on the user's screen with three choices: "Allow" issues a single-use key, "Always Allow" issues a persistent one, and "Deny" refuses. Tell the user to expect the dialog, and to pick "Always Allow" for a session that will perform several writes. The key is stored locally and reused; write tools also re-authorize on their own when a single-use key is spent, so calling this manually is only needed to grant access up front. Calling it when a key is already stored reuses that key and shows no dialog.
| Name | Type | Req | Description |
|---|---|---|---|
| force | boolean | – | Request a new key even though one is already stored. Rarely needed: a stored key is reused, and replacing a persistent key with a single-use one makes things worse. |
| Name | Type | Req | Description |
|---|---|---|---|
| alreadyHeld | boolean | yes | True when an existing stored key was reused and no dialog appeared. |
| authorized | boolean | yes | – |
| message | string | yes | – |
| persistent | boolean | yes | True when the key persists; false keys are consumed by the next write. |
No examples provided.
zotero_create_collection Create collections ~120
Create one or more collections, optionally nested under an existing collection. Up to 50 per call. Creating a nested tree takes one call per level, since a child needs its parent's key. Requires write access; zotero_authorize runs automatically if none has been granted.
| Name | Type | Req | Description |
|---|---|---|---|
| collections | array | yes | Collections to create, at most 50 per call. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| Name | Type | Req | Description |
|---|---|---|---|
| created | array | yes | – |
| failures | array | yes | – |
| libraryVersion | number|null | yes | – |
No examples provided.
zotero_create_items Create items ~158
Create up to 50 items in one call. Set `collections` on an item to file it into collections as it is created, which is cheaper than creating it and moving it afterwards. Notes and attachments are created by setting parentItem. To attach a file from disk use zotero_attach_file instead, which handles the whole attachment protocol. Check the returned `failures` array: Zotero validates each item independently, so some can succeed while others fail.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| items | array | yes | Items to create, at most 50 per call. |
| Name | Type | Req | Description |
|---|---|---|---|
| created | array | yes | – |
| failures | array | yes | – |
| libraryVersion | number|null | yes | – |
No examples provided.
zotero_delete_collection Delete collections ~203
Move collections to Zotero's trash, which is reversible and the default. The items inside are never deleted either way: they stay in the library and in any other collection they belong to. Subcollections follow their parent. Pass permanent: true only on an explicit request from the user, to erase the collections outright with no way back. Note that a trashed collection cannot be listed through the local API, so record the key returned here if it may need restoring.
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKeys | array | yes | Keys of the collections to remove, at most 50 per call. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| permanent | boolean | – | false (default) moves the collections to the trash, which is reversible. true erases them irreversibly. Only pass true on an explicit request from the user. |
| Name | Type | Req | Description |
|---|---|---|---|
| erased | array | yes | Collections erased irreversibly. Empty when permanent is false. |
| libraryVersion | number|null | yes | – |
| notFound | array | yes | – |
| permanent | boolean | yes | – |
| trashed | array | yes | Collections moved to the trash. Empty when permanent is true. |
No examples provided.
zotero_delete_items Delete items ~225
Move up to 50 items to Zotero's trash, where the user can restore them from the Zotero window or with zotero_restore_items. This is the default and it is reversible. Trashed items keep their attachments and files; Zotero empties the trash automatically after 30 days by default. Pass permanent: true only when the user has explicitly asked for an irreversible delete: that erases the items outright, takes their child notes and attachments with them, removes attachment files from disk, and cannot be undone by anything.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| itemKeys | array | yes | Keys of the items to remove, at most 50 per call. |
| permanent | boolean | – | false (default) moves the items to the trash, which is reversible. true erases them immediately and irreversibly, deleting attachment files from disk. Only pass true on an explicit request from the u… |
| Name | Type | Req | Description |
|---|---|---|---|
| alreadyInTrash | array | yes | – |
| erased | array | yes | Items erased irreversibly. Empty when permanent is false. |
| libraryVersion | number|null | yes | – |
| notFound | array | yes | – |
| permanent | boolean | yes | – |
| trashed | array | yes | Items moved to the trash. Empty when permanent is true. |
No examples provided.
zotero_empty_trash Empty the trash ~166
Permanently erase every item in the trash. This is irreversible and removes attachment files from disk. As an interlock against emptying a trash the caller has not looked at, expectedCount must equal the number of items actually in it: call zotero_list_trash first and pass its totalResults. If the two disagree the call is refused and nothing is deleted. Only use this when the user has explicitly asked to empty the trash.
| Name | Type | Req | Description |
|---|---|---|---|
| expectedCount | integer | yes | How many items you expect to erase, from zotero_list_trash. A mismatch aborts the call. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| Name | Type | Req | Description |
|---|---|---|---|
| erased | number | yes | – |
| libraryVersion | number|null | yes | – |
No examples provided.
zotero_export_items Export citations ~222
Export items in a citation format. Use format "bibtex", "biblatex", "ris", "csljson", "csv" or "tei" for a machine-readable export, or "bib" to render a formatted bibliography in a citation style (set `style`, e.g. "apa", "chicago-note-bibliography", "ieee"). Returns the export as text.
| Name | Type | Req | Description |
|---|---|---|---|
| format | string | – | Export format. "bib" renders a formatted bibliography using `style`. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| itemKeys | array | yes | Keys of the items to export. |
| locale | string | – | Locale for format "bib", e.g. "en-US", "es-ES". |
| style | string | – | Citation style for format "bib", e.g. "apa", "ieee", "chicago-note-bibliography". Ignored otherwise. |
| Name | Type | Req | Description |
|---|---|---|---|
| format | string | yes | – |
| itemCount | number | yes | – |
| output | string | yes | – |
| skippedKeys | array | yes | Requested keys that are not top-level items and were left out. |
No examples provided.
zotero_get_attachment_path Get an attachment file path ~130
Resolve an attachment to its absolute path on this machine, so the file can be opened and read directly. Works for both linked and imported attachments. Passing a regular item key returns the paths of all of its file attachments. Use this to read a PDF whose text Zotero has not indexed.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| itemKey | string | yes | Attachment key, or a parent item key to resolve all of its attachments. |
| Name | Type | Req | Description |
|---|---|---|---|
| attachments | array | yes | – |
No examples provided.
zotero_get_collection Get a collection ~157
Fetch one collection by key, including its name, parent collection, item count and current version. The version is what zotero_update_collection and zotero_delete_collection use for conflict detection.
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKey | string | yes | Collection key, e.g. "WXYZ5678". |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| verbose | boolean | – | Return Zotero's raw API envelope (library block, self/alternate links, full meta) instead of the flattened object. Costs many extra tokens per object; only useful when a URL or the raw meta block is… |
| Name | Type | Req | Description |
|---|---|---|---|
| collection | object | yes | – |
No examples provided.
zotero_get_item Get an item ~176
Fetch one item by key with all of its metadata. Set includeChildren to also return its notes and attachments, which is the quickest way to find the attachment key needed to read a PDF or its full text.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| includeChildren | boolean | – | Also return child notes and attachments. |
| itemKey | string | yes | Item key, e.g. "ABCD1234". |
| verbose | boolean | – | Return Zotero's raw API envelope (library block, self/alternate links, full meta) instead of the flattened object. Costs many extra tokens per object; only useful when a URL or the raw meta block is… |
| Name | Type | Req | Description |
|---|---|---|---|
| children | array | – | – |
| item | object | yes | – |
No examples provided.
zotero_get_item_children Get item children ~187
List the child notes, attachments and annotations of an item. Attachment children carry the linkMode and filename needed by zotero_get_attachment_path and zotero_get_item_fulltext.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| itemKey | string | yes | Parent item key. |
| limit | integer | – | Maximum number of objects to return (1-500). |
| start | integer | – | Zero-based offset for paging; pass the nextStart value from a previous call. |
| verbose | boolean | – | Return Zotero's raw API envelope (library block, self/alternate links, full meta) instead of the flattened object. Costs many extra tokens per object; only useful when a URL or the raw meta block is… |
| Name | Type | Req | Description |
|---|---|---|---|
| children | array | yes | – |
| hasMore | boolean | yes | – |
| nextStart | number|null | yes | – |
| returned | number | yes | – |
| start | number | yes | – |
| totalResults | number|null | yes | – |
No examples provided.
zotero_get_item_fulltext Get attachment full text ~149
Return the indexed full text of an attachment. Passing a regular item key works too: its attachments are searched and the first one with indexed text is used. Text comes from Zotero's own index, so it is available only for attachments Zotero has indexed. Long documents can be truncated with maxCharacters.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| itemKey | string | yes | Attachment key, or a parent item key to search its attachments. |
| maxCharacters | integer | – | Truncate the returned text at this many characters. |
| Name | Type | Req | Description |
|---|---|---|---|
| attachmentKey | string | yes | – |
| content | string | yes | – |
| indexedPages | number|null | yes | – |
| totalCharacters | number | yes | – |
| totalPages | number|null | yes | – |
| truncated | boolean | yes | – |
No examples provided.
zotero_get_item_type_fields Describe an item type ~105
List the valid field names and creator types for a Zotero item type, or list every item type when itemType is omitted. Call this before zotero_create_items with an unfamiliar item type: Zotero rejects a write outright if it carries a field the type does not define.
| Name | Type | Req | Description |
|---|---|---|---|
| itemType | string | – | Item type to describe, e.g. "journalArticle", "book", "thesis", "preprint". Omit to list all types. |
| Name | Type | Req | Description |
|---|---|---|---|
| creatorTypes | array | – | – |
| fields | array | – | – |
| itemType | string | – | – |
| itemTypes | array | – | – |
No examples provided.
zotero_list_collections List collections ~256
List collections in a library. scope="all" returns every collection flat (each carrying its parentCollection key, so the full tree can be reconstructed in one call), "top" returns only root-level collections, and "children" returns the direct subcollections of parentKey. Collection keys returned here are what zotero_create_items, zotero_add_items_to_collection and zotero_search_items take.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| limit | integer | – | Maximum number of objects to return (1-500). |
| parentKey | string | – | Parent collection key; required when scope is "children". |
| scope | string | – | Which collections to return. "children" requires parentKey. |
| start | integer | – | Zero-based offset for paging; pass the nextStart value from a previous call. |
| verbose | boolean | – | Return Zotero's raw API envelope (library block, self/alternate links, full meta) instead of the flattened object. Costs many extra tokens per object; only useful when a URL or the raw meta block is… |
| Name | Type | Req | Description |
|---|---|---|---|
| collections | array | yes | – |
| hasMore | boolean | yes | – |
| nextStart | number|null | yes | – |
| returned | number | yes | – |
| start | number | yes | – |
| totalResults | number|null | yes | – |
No examples provided.
zotero_list_libraries List libraries ~46
List the personal library and every group library available locally. Use this to get the groupId that other tools take when the target is a group library rather than "My Library".
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| groups | array | yes | – |
| personalLibrary | – | yes | – |
No examples provided.
zotero_list_saved_searches List saved searches ~184
List the saved searches defined in the library, with their conditions. Run one with zotero_run_saved_search. Note that the local API can actually execute saved searches, which the zotero.org web API cannot.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| limit | integer | – | Maximum number of objects to return (1-500). |
| start | integer | – | Zero-based offset for paging; pass the nextStart value from a previous call. |
| verbose | boolean | – | Return Zotero's raw API envelope (library block, self/alternate links, full meta) instead of the flattened object. Costs many extra tokens per object; only useful when a URL or the raw meta block is… |
| Name | Type | Req | Description |
|---|---|---|---|
| hasMore | boolean | yes | – |
| nextStart | number|null | yes | – |
| returned | number | yes | – |
| searches | array | yes | – |
| start | number | yes | – |
| totalResults | number|null | yes | – |
No examples provided.
zotero_list_tags List tags ~172
List tags in the library, optionally only those used within one collection or matching a search. Useful for discovering how a library is organized before filtering zotero_search_items by tag.
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKey | string | – | Only tags used by items in this collection. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| limit | integer | – | Maximum number of objects to return (1-500). |
| q | string | – | Filter tags by text. |
| qmode | string | – | How `q` matches. Ignored when `q` is omitted. |
| start | integer | – | Zero-based offset for paging; pass the nextStart value from a previous call. |
| Name | Type | Req | Description |
|---|---|---|---|
| hasMore | boolean | yes | – |
| nextStart | number|null | yes | – |
| returned | number | yes | – |
| start | number | yes | – |
| tags | array | yes | – |
| totalResults | number|null | yes | – |
No examples provided.
zotero_list_trash List the trash ~205
List the items currently in Zotero's trash, which are the ones zotero_restore_items can bring back. Note that trashed *collections* do not appear here: Zotero's local API offers no way to enumerate them, so a trashed collection can only be restored by key or from the Zotero window.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| limit | integer | – | Maximum number of objects to return (1-500). |
| start | integer | – | Zero-based offset for paging; pass the nextStart value from a previous call. |
| verbose | boolean | – | Return Zotero's raw API envelope (library block, self/alternate links, full meta) instead of the flattened object. Costs many extra tokens per object; only useful when a URL or the raw meta block is… |
| Name | Type | Req | Description |
|---|---|---|---|
| hasMore | boolean | yes | – |
| items | array | yes | – |
| nextStart | number|null | yes | – |
| returned | number | yes | – |
| start | number | yes | – |
| totalResults | number|null | yes | – |
No examples provided.
zotero_remove_items_from_collection Remove items from a collection ~115
Remove items from one collection. The items stay in the library and in any other collection they belong to; nothing is deleted. Items that were not in the collection are reported as unchanged.
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKey | string | yes | Collection to remove them from. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| itemKeys | array | yes | Keys of the items to remove. |
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKey | string | yes | – |
| notPresent | array | yes | – |
| removed | array | yes | – |
No examples provided.
zotero_restore_collection Restore collections from the trash ~142
Bring collections back out of Zotero's trash, undoing a non-permanent zotero_delete_collection. You must know the key: Zotero's local API cannot list trashed collections, so there is no way to discover them from here, the user can see them in the Zotero window's trash. A collection erased permanently cannot be restored at all.
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKeys | array | yes | Keys of the collections to restore, at most 50 per call. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| Name | Type | Req | Description |
|---|---|---|---|
| notFound | array | yes | – |
| restored | array | yes | – |
No examples provided.
zotero_restore_items Restore items from the trash ~135
Bring items back out of Zotero's trash, undoing a non-permanent zotero_delete_items. The items return to the collections they were in. Only works while they are still in the trash: nothing can recover an item that was erased permanently or that Zotero has already purged after its 30-day retention.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| itemKeys | array | yes | Keys of the items to restore, at most 50 per call. |
| Name | Type | Req | Description |
|---|---|---|---|
| notFound | array | yes | – |
| restored | array | yes | – |
| wereNotInTrash | array | yes | – |
No examples provided.
zotero_run_saved_search Run a saved search ~199
Execute a saved search and return the matching items. The search runs against the local database using Zotero's own engine, so the results match what the saved search shows in the Zotero UI.
| Name | Type | Req | Description |
|---|---|---|---|
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| limit | integer | – | Maximum number of objects to return (1-500). |
| searchKey | string | yes | Saved search key, from zotero_list_saved_searches. |
| start | integer | – | Zero-based offset for paging; pass the nextStart value from a previous call. |
| verbose | boolean | – | Return Zotero's raw API envelope (library block, self/alternate links, full meta) instead of the flattened object. Costs many extra tokens per object; only useful when a URL or the raw meta block is… |
| Name | Type | Req | Description |
|---|---|---|---|
| hasMore | boolean | yes | – |
| items | array | yes | – |
| nextStart | number|null | yes | – |
| returned | number | yes | – |
| start | number | yes | – |
| totalResults | number|null | yes | – |
No examples provided.
zotero_search_items Search items ~437
Search the local Zotero library. `q` runs Zotero's quicksearch: qmode "titleCreatorYear" (default) matches titles, creators and years, while "everything" also matches attachment full text and notes. Filters combine: pass collectionKey to search inside one collection, itemType to restrict by type ("-attachment" excludes a type), tag to filter by tag. Omit `q` to browse. Returns flattened item metadata; use zotero_get_item for one item in full.
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKey | string | – | Restrict the search to one collection. |
| direction | string | – | Sort direction. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| includeTrashed | boolean | – | Include items currently in the trash. |
| itemType | string | – | Item type filter. Supports Zotero syntax: "book", "book || journalArticle", "-attachment". |
| limit | integer | – | Maximum number of objects to return (1-500). |
| q | string | – | Search text. Omit to list items without filtering by text. |
| qmode | string | – | "titleCreatorYear" searches metadata only; "everything" also searches full text and notes (slower). |
| since | integer | – | Return only objects modified after this library version, for incremental syncing. |
| sort | string | – | Sort field. |
| start | integer | – | Zero-based offset for paging; pass the nextStart value from a previous call. |
| tag | string | – | Tag filter. Supports "tag1 || tag2" for OR and a leading "-" to exclude. |
| topLevelOnly | boolean | – | Return only top-level items, hiding child notes and attachments. Set false to include children. |
| verbose | boolean | – | Return Zotero's raw API envelope (library block, self/alternate links, full meta) instead of the flattened object. Costs many extra tokens per object; only useful when a URL or the raw meta block is… |
| Name | Type | Req | Description |
|---|---|---|---|
| hasMore | boolean | yes | – |
| items | array | yes | – |
| nextStart | number|null | yes | – |
| returned | number | yes | – |
| start | number | yes | – |
| totalResults | number|null | yes | – |
No examples provided.
zotero_status Zotero connection status ~55
Check that the local Zotero instance is running and reachable, and report its version, schema version, instance ID, and whether write access has been granted yet. Call this first when any other tool reports a connection or authorization problem.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| apiVersion | string|null | yes | – |
| appName | string | yes | – |
| baseUrl | string | yes | – |
| connected | boolean | yes | – |
| groupLibraries | number | yes | – |
| personalLibrary | – | yes | – |
| schemaVersion | string|null | yes | – |
| serverId | string | yes | – |
| writeAccess | boolean | yes | – |
| zoteroVersion | string|null | yes | – |
No examples provided.
zotero_update_collection Rename or move a collection ~193
Rename a collection and/or move it under a different parent. Pass parentCollectionKey=null to move a collection to the root of the library. The current version is fetched automatically unless expectedVersion is given, in which case the write fails with a conflict if the collection changed in the meantime.
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKey | string | yes | Key of the collection to modify. |
| expectedVersion | integer | – | Version the collection is expected to be at, for conflict detection. Omit to use the current version. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| name | string | – | New name. Omit to leave the name unchanged. |
| parentCollectionKey | – | – | New parent collection key, or null to move to the library root. Omit to leave the parent unchanged. |
| Name | Type | Req | Description |
|---|---|---|---|
| collectionKey | string | yes | – |
| libraryVersion | number|null | yes | – |
| updated | boolean | yes | – |
No examples provided.
zotero_update_item Update an item ~214
Patch fields on an existing item. Only the fields passed in `fields` change; everything else is left alone. Array fields are replaced wholesale, so to change collection membership prefer zotero_add_items_to_collection / zotero_remove_items_from_collection, which merge instead of overwriting. The current version is read automatically unless expectedVersion is supplied.
| Name | Type | Req | Description |
|---|---|---|---|
| expectedVersion | integer | – | Version the item is expected to be at, for conflict detection. Omit to use the current version. |
| fields | object | yes | Fields to set, as Zotero item JSON, e.g. {"title": "New title", "date": "2024", "tags": [{"tag": "to-read"}]}. Field names must be valid for the item's type. |
| groupId | integer | – | Group library ID. Omit for the personal library ("My Library"), which is what almost every request wants. Group IDs come from zotero_list_libraries. |
| itemKey | string | yes | Key of the item to update. |
| Name | Type | Req | Description |
|---|---|---|---|
| itemKey | string | yes | – |
| libraryVersion | number|null | yes | – |
| updated | boolean | yes | – |
No examples provided.
What is the Zotero Native MCP server?
Zotero Native MCP is listed in the public MCP registry as io.github.dvdsosa/zotero-native-mcp. Read and write your local Zotero 10 library. No plugin, no zotero.org API key, no cloud. This page covers its npm package (zotero-native-mcp).
Is the Zotero Native MCP server safe to use?
Zotero Native MCP scores 88 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 21 September 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the Zotero Native MCP server expose?
Zotero Native MCP exposes 28 tools: zotero_status, zotero_authorize, zotero_list_libraries, zotero_get_item_type_fields, zotero_list_collections, and 23 more. Their descriptions and schemas cost roughly 4,987 tokens of context every time the server is loaded.
Is the Zotero Native MCP server still maintained?
Zotero Native MCP is still listed as active in the MCP registry. We last reached this channel on 21 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.
What licence is the Zotero Native MCP server under?
Zotero Native MCP declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.