# Zotero Native MCP (npm · zotero-native-mcp)

Read and write your local Zotero 10 library. No plugin, no zotero.org API key, no cloud.

- Trust score: 88/100 (high trust)
- Change this week: +4
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-21

## Components

- npm · `zotero-native-mcp`: 88/100 (this document), [markdown](https://verifymcp.io/servers/dvdsosa-zotero-native-mcp/zotero-native-mcp.md), [page](https://verifymcp.io/servers/dvdsosa-zotero-native-mcp/zotero-native-mcp)

## Channel facts

- Registry: `npm`
- Package: `zotero-native-mcp`
- Version: `1.0.1`
- Transport: `stdio`

## Trust breakdown

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. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-21.

- **Supply Chain Security**: 98/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 31 of 96 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 97/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to dvdsosa/zotero-native-mcp).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 17 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 71/100
  - AI-judged instruction clarity (excellent).
  - 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.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 60/100
  - Stability observed for 18 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 94/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 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.
  - An AI judge read all 29 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### 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.

### Claude

```bash
claude mcp add dvdsosa-zotero-native-mcp -- npx -y zotero-native-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "dvdsosa-zotero-native-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "zotero-native-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "dvdsosa-zotero-native-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "zotero-native-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add dvdsosa-zotero-native-mcp -- npx -y zotero-native-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "dvdsosa-zotero-native-mcp": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "zotero-native-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add dvdsosa-zotero-native-mcp --command npx --arg -y --arg zotero-native-mcp
```

### Hermes

```yaml
mcp_servers:
  dvdsosa-zotero-native-mcp:
    command: "npx"
    args: ["-y", "zotero-native-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "dvdsosa-zotero-native-mcp": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "zotero-native-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add dvdsosa-zotero-native-mcp -t stdio -c npx -a -y zotero-native-mcp
```

### Other

```json
{
  "mcpServers": {
    "dvdsosa-zotero-native-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "zotero-native-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-21 (score 88, +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.

### 2026-09-19 (score 87, +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.

### 2026-09-17 (score 86, +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.

### 2026-09-15 (score 85, +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.

### 2026-09-13 (score 84, +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.

### 2026-09-11 (score 83, +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.

### 2026-09-08 (score 82, +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.

### 2026-09-06 (score 81, +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.

## MCP tools (28)

### `zotero_status` (~55 tokens)

Zotero connection status

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.

Output parameters:

- `apiVersion` (string|null)
- `appName` (string)
- `baseUrl` (string)
- `connected` (boolean)
- `groupLibraries` (number)
- `personalLibrary`
- `schemaVersion` (string|null)
- `serverId` (string)
- `writeAccess` (boolean)
- `zoteroVersion` (string|null)

### `zotero_authorize` (~191 tokens)

Request Zotero write access

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.

Input parameters:

- `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.

Output parameters:

- `alreadyHeld` (boolean): True when an existing stored key was reused and no dialog appeared.
- `authorized` (boolean)
- `message` (string)
- `persistent` (boolean): True when the key persists; false keys are consumed by the next write.

### `zotero_list_libraries` (~46 tokens)

List libraries

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".

Output parameters:

- `groups` (array)
- `personalLibrary`

### `zotero_get_item_type_fields` (~105 tokens)

Describe an item type

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.

Input parameters:

- `itemType` (string): Item type to describe, e.g. "journalArticle", "book", "thesis", "preprint". Omit to list all types.

Output parameters:

- `creatorTypes` (array)
- `fields` (array)
- `itemType` (string)
- `itemTypes` (array)

### `zotero_list_collections` (~256 tokens)

List collections

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.

Input parameters:

- `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…

Output parameters:

- `collections` (array)
- `hasMore` (boolean)
- `nextStart` (number|null)
- `returned` (number)
- `start` (number)
- `totalResults` (number|null)

### `zotero_get_collection` (~157 tokens)

Get a collection

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.

Input parameters:

- `collectionKey` (string, required): 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…

Output parameters:

- `collection` (object)

### `zotero_create_collection` (~120 tokens)

Create collections

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.

Input parameters:

- `collections` (array, required): 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.

Output parameters:

- `created` (array)
- `failures` (array)
- `libraryVersion` (number|null)

### `zotero_update_collection` (~193 tokens)

Rename or move a collection

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.

Input parameters:

- `collectionKey` (string, required): 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.

Output parameters:

- `collectionKey` (string)
- `libraryVersion` (number|null)
- `updated` (boolean)

### `zotero_delete_collection` (~203 tokens)

Delete collections

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.

Input parameters:

- `collectionKeys` (array, required): 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.

Output parameters:

- `erased` (array): Collections erased irreversibly. Empty when permanent is false.
- `libraryVersion` (number|null)
- `notFound` (array)
- `permanent` (boolean)
- `trashed` (array): Collections moved to the trash. Empty when permanent is true.

### `zotero_restore_collection` (~142 tokens)

Restore collections from the trash

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.

Input parameters:

- `collectionKeys` (array, required): 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.

Output parameters:

- `notFound` (array)
- `restored` (array)

### `zotero_search_items` (~437 tokens)

Search items

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.

Input parameters:

- `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…

Output parameters:

- `hasMore` (boolean)
- `items` (array)
- `nextStart` (number|null)
- `returned` (number)
- `start` (number)
- `totalResults` (number|null)

### `zotero_get_item` (~176 tokens)

Get an item

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.

Input parameters:

- `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, required): 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…

Output parameters:

- `children` (array)
- `item` (object)

### `zotero_get_item_children` (~187 tokens)

Get item children

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.

Input parameters:

- `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, required): 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…

Output parameters:

- `children` (array)
- `hasMore` (boolean)
- `nextStart` (number|null)
- `returned` (number)
- `start` (number)
- `totalResults` (number|null)

### `zotero_create_items` (~158 tokens)

Create items

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.

Input parameters:

- `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, required): Items to create, at most 50 per call.

Output parameters:

- `created` (array)
- `failures` (array)
- `libraryVersion` (number|null)

### `zotero_update_item` (~214 tokens)

Update an item

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.

Input parameters:

- `expectedVersion` (integer): Version the item is expected to be at, for conflict detection. Omit to use the current version.
- `fields` (object, required): 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, required): Key of the item to update.

Output parameters:

- `itemKey` (string)
- `libraryVersion` (number|null)
- `updated` (boolean)

### `zotero_delete_items` (~225 tokens)

Delete items

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.

Input parameters:

- `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, required): 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…

Output parameters:

- `alreadyInTrash` (array)
- `erased` (array): Items erased irreversibly. Empty when permanent is false.
- `libraryVersion` (number|null)
- `notFound` (array)
- `permanent` (boolean)
- `trashed` (array): Items moved to the trash. Empty when permanent is true.

### `zotero_restore_items` (~135 tokens)

Restore items from the trash

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.

Input parameters:

- `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, required): Keys of the items to restore, at most 50 per call.

Output parameters:

- `notFound` (array)
- `restored` (array)
- `wereNotInTrash` (array)

### `zotero_list_trash` (~205 tokens)

List the trash

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.

Input parameters:

- `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…

Output parameters:

- `hasMore` (boolean)
- `items` (array)
- `nextStart` (number|null)
- `returned` (number)
- `start` (number)
- `totalResults` (number|null)

### `zotero_empty_trash` (~166 tokens)

Empty the trash

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.

Input parameters:

- `expectedCount` (integer, required): 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.

Output parameters:

- `erased` (number)
- `libraryVersion` (number|null)

### `zotero_add_items_to_collection` (~125 tokens)

Add items to a collection

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.

Input parameters:

- `collectionKey` (string, required): 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, required): Keys of the items to file.

Output parameters:

- `added` (array)
- `alreadyPresent` (array)
- `collectionKey` (string)

### `zotero_remove_items_from_collection` (~115 tokens)

Remove items from a collection

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.

Input parameters:

- `collectionKey` (string, required): 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, required): Keys of the items to remove.

Output parameters:

- `collectionKey` (string)
- `notPresent` (array)
- `removed` (array)

### `zotero_get_item_fulltext` (~149 tokens)

Get attachment full text

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.

Input parameters:

- `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, required): Attachment key, or a parent item key to search its attachments.
- `maxCharacters` (integer): Truncate the returned text at this many characters.

Output parameters:

- `attachmentKey` (string)
- `content` (string)
- `indexedPages` (number|null)
- `totalCharacters` (number)
- `totalPages` (number|null)
- `truncated` (boolean)

### `zotero_export_items` (~222 tokens)

Export citations

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.

Input parameters:

- `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, required): 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.

Output parameters:

- `format` (string)
- `itemCount` (number)
- `output` (string)
- `skippedKeys` (array): Requested keys that are not top-level items and were left out.

### `zotero_attach_file` (~320 tokens)

Attach a local file

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.

Input parameters:

- `collections` (array): Collections to file the attachment into. Only valid for standalone attachments (no parentItemKey).
- `filePath` (string, required): 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.

Output parameters:

- `attachment` (object)
- `attachmentKey` (string)
- `bytes` (number)
- `contentType` (string)
- `filePath` (string)
- `mode` (string)
- `uploaded` (boolean): True when file bytes were transferred; false for linked files and deduplicated uploads.

### `zotero_get_attachment_path` (~130 tokens)

Get an attachment file path

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.

Input parameters:

- `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, required): Attachment key, or a parent item key to resolve all of its attachments.

Output parameters:

- `attachments` (array)

### `zotero_list_tags` (~172 tokens)

List tags

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.

Input parameters:

- `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.

Output parameters:

- `hasMore` (boolean)
- `nextStart` (number|null)
- `returned` (number)
- `start` (number)
- `tags` (array)
- `totalResults` (number|null)

### `zotero_list_saved_searches` (~184 tokens)

List saved searches

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.

Input parameters:

- `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…

Output parameters:

- `hasMore` (boolean)
- `nextStart` (number|null)
- `returned` (number)
- `searches` (array)
- `start` (number)
- `totalResults` (number|null)

### `zotero_run_saved_search` (~199 tokens)

Run a saved search

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.

Input parameters:

- `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, required): 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…

Output parameters:

- `hasMore` (boolean)
- `items` (array)
- `nextStart` (number|null)
- `returned` (number)
- `start` (number)
- `totalResults` (number|null)

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/dvdsosa-zotero-native-mcp/zotero-native-mcp#diagnostics

## Score history

- 2026-09-21: 88
- 2026-09-20: 87
- 2026-09-19: 87
- 2026-09-18: 86
- 2026-09-17: 86
- 2026-09-16: 85
- 2026-09-15: 85
- 2026-09-14: 84
- 2026-09-13: 84
- 2026-09-12: 83
- 2026-09-11: 83
- 2026-09-10: 82
- 2026-09-09: 82
- 2026-09-08: 82
- 2026-09-07: 81
- 2026-09-06: 81
- 2026-09-05: 80
- 2026-09-04: 80
- 2026-09-03: 54

## Common questions

### 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.

## Links

- npm package: https://www.npmjs.com/package/zotero-native-mcp
- Socket report: https://socket.dev/npm/package/zotero-native-mcp
- Repository: https://github.com/dvdsosa/zotero-native-mcp
- Changelog RSS feed: https://verifymcp.io/servers/dvdsosa-zotero-native-mcp/zotero-native-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/dvdsosa-zotero-native-mcp/zotero-native-mcp.json
- HTML version of this page: https://verifymcp.io/servers/dvdsosa-zotero-native-mcp/zotero-native-mcp
