@xevos117/mcp-zotero 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -5
- package/build/tools/add-items-by-doi.js +24 -5
- package/build/utils/csl-to-zotero.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,12 +17,18 @@ For advanced use cases (PDF upload policy, citation style guidance, source trans
|
|
|
17
17
|
|
|
18
18
|
| Scenario | MCP server | Skill needed? |
|
|
19
19
|
|---|---|---|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
20
|
+
| LLM with filesystem access (Claude Code, LM Studio, etc.) | All 15 tools | No |
|
|
21
|
+
| LLM without filesystem access (Claude.ai Projects, Claude Desktop) | API tools (search, add, metadata) | Yes, for citation injection |
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
LLMs with filesystem access can use all tools directly, including `inject_citations` which reads and writes `.docx` files on disk.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
LLMs without filesystem access — including Claude Desktop, which connects to MCP but cannot generate files locally — can use the included **Claude skill** (`skills/zotero-skill-mcp-integrations/`), which runs citation injection entirely inside a sandbox. MCP tools handle all Zotero API operations; the skill handles document assembly.
|
|
26
|
+
|
|
27
|
+
### Claude Skill Setup (for Claude.ai Projects and Claude Desktop)
|
|
28
|
+
|
|
29
|
+
1. Download the skill `.zip` from the latest [GitHub Release](https://github.com/Xevos117/mcp-zotero/releases)
|
|
30
|
+
2. Extract it and upload the folder to your Claude.ai Project as a skill
|
|
31
|
+
3. The skill enables citation injection directly inside the sandbox, without requiring local filesystem access
|
|
26
32
|
|
|
27
33
|
## Setup
|
|
28
34
|
|
|
@@ -159,7 +165,7 @@ claude mcp add-json "zotero" '{"command":"npx","args":["tsx","src/server.ts"],"e
|
|
|
159
165
|
```bash
|
|
160
166
|
npm install
|
|
161
167
|
npm run build # Compile TypeScript
|
|
162
|
-
npm test # Run tests (vitest,
|
|
168
|
+
npm test # Run tests (vitest, 382 tests)
|
|
163
169
|
npx tsx src/server.ts # Run directly without building
|
|
164
170
|
```
|
|
165
171
|
|
|
@@ -105,12 +105,31 @@ export async function handleAddItemsByDoi(zoteroApi, userId, args) {
|
|
|
105
105
|
.library("user", userId)
|
|
106
106
|
.items()
|
|
107
107
|
.post(zoteroItems);
|
|
108
|
+
if (!response.isSuccess()) {
|
|
109
|
+
const errors = response.getErrors();
|
|
110
|
+
const errorMessages = Object.entries(errors)
|
|
111
|
+
.map(([idx, msg]) => `Item ${idx}: ${typeof msg === "object" ? JSON.stringify(msg) : msg}`)
|
|
112
|
+
.join(", ");
|
|
113
|
+
return formatErrorResponse(`Zotero API write failed: ${errorMessages}`);
|
|
114
|
+
}
|
|
108
115
|
const createdItems = response.getData();
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
if (!createdItems || !Array.isArray(createdItems) || createdItems.length === 0) {
|
|
117
|
+
return formatErrorResponse("Zotero API returned empty response - items may not have been created");
|
|
118
|
+
}
|
|
119
|
+
const success = [];
|
|
120
|
+
for (let i = 0; i < resolved.success.length; i++) {
|
|
121
|
+
const r = resolved.success[i];
|
|
122
|
+
const entity = response.getEntityByIndex(i);
|
|
123
|
+
const itemKey = entity?.key;
|
|
124
|
+
if (!itemKey) {
|
|
125
|
+
return formatErrorResponse(`Failed to get item key for DOI ${r.doi} at index ${i}. API response may be invalid.`);
|
|
126
|
+
}
|
|
127
|
+
success.push({
|
|
128
|
+
doi: r.doi,
|
|
129
|
+
item_key: itemKey,
|
|
130
|
+
title: entity?.title ?? r.data.title ?? "Untitled",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
114
133
|
let pdf_results;
|
|
115
134
|
if (auto_attach_pdf) {
|
|
116
135
|
const apiKey = process.env.ZOTERO_API_KEY;
|
|
@@ -101,8 +101,8 @@ export function cslToZoteroItem(csl, options) {
|
|
|
101
101
|
place: csl["publisher-place"] ?? "",
|
|
102
102
|
url: csl.URL ?? "",
|
|
103
103
|
abstractNote: csl.abstract ?? "",
|
|
104
|
-
ISBN: csl.ISBN ?? "",
|
|
105
|
-
ISSN: csl.ISSN ?? "",
|
|
104
|
+
ISBN: Array.isArray(csl.ISBN) ? csl.ISBN[0] : csl.ISBN ?? "",
|
|
105
|
+
ISSN: Array.isArray(csl.ISSN) ? csl.ISSN[0] : csl.ISSN ?? "",
|
|
106
106
|
edition: csl.edition ?? "",
|
|
107
107
|
numPages: csl["number-of-pages"] ?? "",
|
|
108
108
|
series: csl["collection-title"] ?? "",
|