zotero-bridge 1.1.0 → 1.1.1
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/dist/database.d.ts +48 -0
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +299 -0
- package/dist/database.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +303 -251
- package/dist/index.js.map +1 -1
- package/dist/tools.d.ts +123 -284
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +119 -326
- package/dist/tools.js.map +1 -1
- package/package.json +2 -2
- package/src/database.ts +335 -0
- package/src/index.ts +290 -328
- package/src/tools.ts +122 -357
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* SQLite database for collection management, tagging, PDF reading, and more.
|
|
8
8
|
*
|
|
9
9
|
* @author Combjellyshen
|
|
10
|
-
* @version 1.
|
|
10
|
+
* @version 1.1.0 (Consolidated tools)
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
@@ -22,55 +22,24 @@ import { ZoteroDatabase } from './database.js';
|
|
|
22
22
|
import { PDFProcessor } from './pdf.js';
|
|
23
23
|
import {
|
|
24
24
|
toolDefinitions,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
createCollectionSchema,
|
|
28
|
-
renameCollectionSchema,
|
|
29
|
-
moveCollectionSchema,
|
|
30
|
-
deleteCollectionSchema,
|
|
31
|
-
getSubcollectionsSchema,
|
|
32
|
-
listTagsSchema,
|
|
33
|
-
addTagSchema,
|
|
34
|
-
removeTagSchema,
|
|
35
|
-
getItemTagsSchema,
|
|
36
|
-
createTagSchema,
|
|
25
|
+
manageCollectionSchema,
|
|
26
|
+
manageTagsSchema,
|
|
37
27
|
searchItemsSchema,
|
|
38
28
|
getItemDetailsSchema,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
getCollectionItemsSchema,
|
|
42
|
-
getItemAbstractSchema,
|
|
43
|
-
setItemAbstractSchema,
|
|
44
|
-
getItemNotesSchema,
|
|
45
|
-
addItemNoteSchema,
|
|
46
|
-
extractPDFTextSchema,
|
|
47
|
-
getPDFSummarySchema,
|
|
48
|
-
getItemPDFsSchema,
|
|
49
|
-
searchPDFSchema,
|
|
50
|
-
generateAbstractFromPDFSchema,
|
|
51
|
-
getDatabaseInfoSchema,
|
|
52
|
-
rawQuerySchema,
|
|
53
|
-
// New schemas
|
|
54
|
-
findByDOISchema,
|
|
55
|
-
findByISBNSchema,
|
|
29
|
+
manageItemContentSchema,
|
|
30
|
+
managePDFSchema,
|
|
56
31
|
findByIdentifierSchema,
|
|
57
|
-
|
|
58
|
-
getAttachmentAnnotationsSchema,
|
|
59
|
-
getAnnotationsByTypeSchema,
|
|
60
|
-
getAnnotationsByColorSchema,
|
|
61
|
-
searchAnnotationsSchema,
|
|
32
|
+
getAnnotationsSchema,
|
|
62
33
|
searchFulltextSchema,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
findSimilarByCreatorsSchema,
|
|
68
|
-
findSimilarByCollectionSchema
|
|
34
|
+
findRelatedItemsSchema,
|
|
35
|
+
getDatabaseInfoSchema,
|
|
36
|
+
rawQuerySchema,
|
|
37
|
+
libraryMaintenanceSchema
|
|
69
38
|
} from './tools.js';
|
|
70
39
|
|
|
71
40
|
// Server configuration
|
|
72
41
|
const SERVER_NAME = 'zotero-bridge';
|
|
73
|
-
const SERVER_VERSION = '1.
|
|
42
|
+
const SERVER_VERSION = '1.1.0';
|
|
74
43
|
|
|
75
44
|
// Parse command line arguments
|
|
76
45
|
function parseArgs(): { dbPath?: string; readonly: boolean } {
|
|
@@ -112,7 +81,7 @@ async function main() {
|
|
|
112
81
|
|
|
113
82
|
// Initialize database
|
|
114
83
|
const db = new ZoteroDatabase(dbPath, readonly);
|
|
115
|
-
await db.connect();
|
|
84
|
+
await db.connect();
|
|
116
85
|
const pdf = new PDFProcessor(db);
|
|
117
86
|
|
|
118
87
|
// Create MCP server
|
|
@@ -163,97 +132,93 @@ async function main() {
|
|
|
163
132
|
|
|
164
133
|
switch (name) {
|
|
165
134
|
// ============================================
|
|
166
|
-
// Collection
|
|
135
|
+
// Collection Management (Consolidated)
|
|
167
136
|
// ============================================
|
|
168
|
-
case '
|
|
169
|
-
const params =
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
137
|
+
case 'manage_collection': {
|
|
138
|
+
const params = manageCollectionSchema.parse(args);
|
|
139
|
+
|
|
140
|
+
switch (params.action) {
|
|
141
|
+
case 'list':
|
|
142
|
+
result = db.getCollections(params.libraryID);
|
|
143
|
+
break;
|
|
144
|
+
case 'get':
|
|
145
|
+
if (params.collectionID) {
|
|
146
|
+
result = db.getCollectionById(params.collectionID);
|
|
147
|
+
} else if (params.name) {
|
|
148
|
+
result = db.getCollectionByName(params.name, params.libraryID);
|
|
149
|
+
} else {
|
|
150
|
+
throw new Error('Either collectionID or name is required for get action');
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
case 'create':
|
|
154
|
+
if (!params.name) throw new Error('Name is required for create action');
|
|
155
|
+
const collectionID = db.createCollection(params.name, params.parentCollectionID || null, params.libraryID);
|
|
156
|
+
result = { success: true, collectionID };
|
|
157
|
+
break;
|
|
158
|
+
case 'rename':
|
|
159
|
+
if (!params.collectionID || !params.newName) throw new Error('collectionID and newName are required');
|
|
160
|
+
result = { success: db.renameCollection(params.collectionID, params.newName) };
|
|
161
|
+
break;
|
|
162
|
+
case 'move':
|
|
163
|
+
if (!params.collectionID) throw new Error('collectionID is required');
|
|
164
|
+
result = { success: db.moveCollection(params.collectionID, params.parentCollectionID ?? null) };
|
|
165
|
+
break;
|
|
166
|
+
case 'delete':
|
|
167
|
+
if (!params.collectionID) throw new Error('collectionID is required');
|
|
168
|
+
result = { success: db.deleteCollection(params.collectionID) };
|
|
169
|
+
break;
|
|
170
|
+
case 'get_subcollections':
|
|
171
|
+
if (!params.collectionID) throw new Error('collectionID is required');
|
|
172
|
+
result = db.getSubcollections(params.collectionID);
|
|
173
|
+
break;
|
|
174
|
+
case 'add_item':
|
|
175
|
+
if (!params.itemID || !params.collectionID) throw new Error('itemID and collectionID are required');
|
|
176
|
+
result = { success: db.addItemToCollection(params.itemID, params.collectionID) };
|
|
177
|
+
break;
|
|
178
|
+
case 'remove_item':
|
|
179
|
+
if (!params.itemID || !params.collectionID) throw new Error('itemID and collectionID are required');
|
|
180
|
+
result = { success: db.removeItemFromCollection(params.itemID, params.collectionID) };
|
|
181
|
+
break;
|
|
182
|
+
case 'get_items':
|
|
183
|
+
if (!params.collectionID) throw new Error('collectionID is required');
|
|
184
|
+
result = db.getCollectionItems(params.collectionID);
|
|
185
|
+
break;
|
|
186
|
+
default:
|
|
187
|
+
throw new Error(`Unknown action: ${params.action}`);
|
|
182
188
|
}
|
|
183
189
|
break;
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
case 'create_collection': {
|
|
187
|
-
const params = createCollectionSchema.parse(args);
|
|
188
|
-
const collectionID = db.createCollection(
|
|
189
|
-
params.name,
|
|
190
|
-
params.parentCollectionID || null,
|
|
191
|
-
params.libraryID
|
|
192
|
-
);
|
|
193
|
-
result = { success: true, collectionID };
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
case 'rename_collection': {
|
|
198
|
-
const params = renameCollectionSchema.parse(args);
|
|
199
|
-
const success = db.renameCollection(params.collectionID, params.newName);
|
|
200
|
-
result = { success };
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
case 'move_collection': {
|
|
205
|
-
const params = moveCollectionSchema.parse(args);
|
|
206
|
-
const success = db.moveCollection(params.collectionID, params.newParentID);
|
|
207
|
-
result = { success };
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
case 'delete_collection': {
|
|
212
|
-
const params = deleteCollectionSchema.parse(args);
|
|
213
|
-
const success = db.deleteCollection(params.collectionID);
|
|
214
|
-
result = { success };
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
case 'get_subcollections': {
|
|
219
|
-
const params = getSubcollectionsSchema.parse(args);
|
|
220
|
-
result = db.getSubcollections(params.parentCollectionID);
|
|
221
|
-
break;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
192
|
// ============================================
|
|
225
|
-
// Tag
|
|
193
|
+
// Tag Management (Consolidated)
|
|
226
194
|
// ============================================
|
|
227
|
-
case '
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const params = createTagSchema.parse(args);
|
|
255
|
-
const tagID = db.createTag(params.name, params.type);
|
|
256
|
-
result = { success: true, tagID };
|
|
195
|
+
case 'manage_tags': {
|
|
196
|
+
const params = manageTagsSchema.parse(args);
|
|
197
|
+
|
|
198
|
+
switch (params.action) {
|
|
199
|
+
case 'list':
|
|
200
|
+
result = db.getTags();
|
|
201
|
+
break;
|
|
202
|
+
case 'get_item_tags':
|
|
203
|
+
if (!params.itemID) throw new Error('itemID is required');
|
|
204
|
+
result = db.getItemTags(params.itemID);
|
|
205
|
+
break;
|
|
206
|
+
case 'add':
|
|
207
|
+
if (!params.itemID || !params.tagName) throw new Error('itemID and tagName are required');
|
|
208
|
+
result = { success: db.addTagToItem(params.itemID, params.tagName, params.type) };
|
|
209
|
+
break;
|
|
210
|
+
case 'remove':
|
|
211
|
+
if (!params.itemID || !params.tagName) throw new Error('itemID and tagName are required');
|
|
212
|
+
result = { success: db.removeTagFromItem(params.itemID, params.tagName) };
|
|
213
|
+
break;
|
|
214
|
+
case 'create':
|
|
215
|
+
if (!params.tagName) throw new Error('tagName is required');
|
|
216
|
+
const tagID = db.createTag(params.tagName, params.type);
|
|
217
|
+
result = { success: true, tagID };
|
|
218
|
+
break;
|
|
219
|
+
default:
|
|
220
|
+
throw new Error(`Unknown action: ${params.action}`);
|
|
221
|
+
}
|
|
257
222
|
break;
|
|
258
223
|
}
|
|
259
224
|
|
|
@@ -272,125 +237,190 @@ async function main() {
|
|
|
272
237
|
result = db.getItemDetails(params.itemID);
|
|
273
238
|
} else if (params.itemKey) {
|
|
274
239
|
const item = db.getItemByKey(params.itemKey);
|
|
275
|
-
|
|
276
|
-
result = db.getItemDetails(item.itemID);
|
|
277
|
-
} else {
|
|
278
|
-
result = null;
|
|
279
|
-
}
|
|
240
|
+
result = item ? db.getItemDetails(item.itemID) : null;
|
|
280
241
|
} else {
|
|
281
242
|
throw new Error('Either itemID or itemKey is required');
|
|
282
243
|
}
|
|
283
244
|
break;
|
|
284
245
|
}
|
|
285
246
|
|
|
286
|
-
case 'add_item_to_collection': {
|
|
287
|
-
const params = addItemToCollectionSchema.parse(args);
|
|
288
|
-
const success = db.addItemToCollection(params.itemID, params.collectionID);
|
|
289
|
-
result = { success };
|
|
290
|
-
break;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
case 'remove_item_from_collection': {
|
|
294
|
-
const params = removeItemFromCollectionSchema.parse(args);
|
|
295
|
-
const success = db.removeItemFromCollection(params.itemID, params.collectionID);
|
|
296
|
-
result = { success };
|
|
297
|
-
break;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
case 'get_collection_items': {
|
|
301
|
-
const params = getCollectionItemsSchema.parse(args);
|
|
302
|
-
result = db.getCollectionItems(params.collectionID);
|
|
303
|
-
break;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
247
|
// ============================================
|
|
307
|
-
// Abstract/Note
|
|
248
|
+
// Abstract/Note Management (Consolidated)
|
|
308
249
|
// ============================================
|
|
309
|
-
case '
|
|
310
|
-
const params =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
result = { success: true, noteID };
|
|
250
|
+
case 'manage_item_content': {
|
|
251
|
+
const params = manageItemContentSchema.parse(args);
|
|
252
|
+
|
|
253
|
+
switch (params.action) {
|
|
254
|
+
case 'get_abstract':
|
|
255
|
+
result = { abstract: db.getItemAbstract(params.itemID) };
|
|
256
|
+
break;
|
|
257
|
+
case 'set_abstract':
|
|
258
|
+
if (!params.abstract) throw new Error('abstract is required');
|
|
259
|
+
result = { success: db.setItemAbstract(params.itemID, params.abstract) };
|
|
260
|
+
break;
|
|
261
|
+
case 'get_notes':
|
|
262
|
+
result = db.getItemNotes(params.itemID);
|
|
263
|
+
break;
|
|
264
|
+
case 'add_note':
|
|
265
|
+
if (!params.noteContent) throw new Error('noteContent is required');
|
|
266
|
+
const noteID = db.addItemNote(params.itemID, params.noteContent, params.noteTitle);
|
|
267
|
+
result = { success: true, noteID };
|
|
268
|
+
break;
|
|
269
|
+
default:
|
|
270
|
+
throw new Error(`Unknown action: ${params.action}`);
|
|
271
|
+
}
|
|
332
272
|
break;
|
|
333
273
|
}
|
|
334
274
|
|
|
335
275
|
// ============================================
|
|
336
|
-
// PDF
|
|
276
|
+
// PDF Management (Consolidated)
|
|
337
277
|
// ============================================
|
|
338
|
-
case '
|
|
339
|
-
const params =
|
|
340
|
-
|
|
278
|
+
case 'manage_pdf': {
|
|
279
|
+
const params = managePDFSchema.parse(args);
|
|
280
|
+
|
|
281
|
+
switch (params.action) {
|
|
282
|
+
case 'extract_text':
|
|
283
|
+
if (!params.attachmentItemID) throw new Error('attachmentItemID is required');
|
|
284
|
+
result = await pdf.extractTextFromAttachment(params.attachmentItemID);
|
|
285
|
+
break;
|
|
286
|
+
case 'get_summary':
|
|
287
|
+
if (!params.attachmentItemID) throw new Error('attachmentItemID is required');
|
|
288
|
+
result = await pdf.getPDFSummary(params.attachmentItemID);
|
|
289
|
+
break;
|
|
290
|
+
case 'list':
|
|
291
|
+
if (!params.parentItemID) throw new Error('parentItemID is required');
|
|
292
|
+
const attachments = db.getPDFAttachments(params.parentItemID);
|
|
293
|
+
result = attachments.map(att => ({
|
|
294
|
+
...att,
|
|
295
|
+
fullPath: db.getAttachmentPath(att.itemID)
|
|
296
|
+
}));
|
|
297
|
+
break;
|
|
298
|
+
case 'search':
|
|
299
|
+
if (!params.attachmentItemID || !params.query) throw new Error('attachmentItemID and query are required');
|
|
300
|
+
const content = await pdf.extractTextFromAttachment(params.attachmentItemID);
|
|
301
|
+
result = content ? pdf.searchInPDF(content, params.query, params.caseSensitive) : [];
|
|
302
|
+
break;
|
|
303
|
+
case 'generate_abstract':
|
|
304
|
+
if (!params.attachmentItemID) throw new Error('attachmentItemID is required');
|
|
305
|
+
const pdfContent = await pdf.extractTextFromAttachment(params.attachmentItemID);
|
|
306
|
+
if (!pdfContent) throw new Error('Could not extract PDF content');
|
|
307
|
+
const abstract = pdf.generateSimpleSummary(pdfContent, params.maxLength);
|
|
308
|
+
if (params.saveToItem) {
|
|
309
|
+
const attDetails = db.query(
|
|
310
|
+
'SELECT parentItemID FROM itemAttachments WHERE itemID = ?',
|
|
311
|
+
[params.attachmentItemID]
|
|
312
|
+
)[0] as { parentItemID: number } | undefined;
|
|
313
|
+
if (attDetails?.parentItemID) {
|
|
314
|
+
db.setItemAbstract(attDetails.parentItemID, abstract);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
result = { abstract, length: abstract.length };
|
|
318
|
+
break;
|
|
319
|
+
default:
|
|
320
|
+
throw new Error(`Unknown action: ${params.action}`);
|
|
321
|
+
}
|
|
341
322
|
break;
|
|
342
323
|
}
|
|
343
324
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
325
|
+
// ============================================
|
|
326
|
+
// Identifier Lookup (Consolidated)
|
|
327
|
+
// ============================================
|
|
328
|
+
case 'find_by_identifier': {
|
|
329
|
+
const params = findByIdentifierSchema.parse(args);
|
|
330
|
+
const identifier = params.identifier.trim();
|
|
331
|
+
let type = params.type;
|
|
332
|
+
|
|
333
|
+
// Auto-detect type
|
|
334
|
+
if (type === 'auto') {
|
|
335
|
+
if (/^10\.\d+\//.test(identifier) || /doi\.org/i.test(identifier)) {
|
|
336
|
+
type = 'doi';
|
|
337
|
+
} else if (/^(97[89])?\d{9}[\dXx]$/.test(identifier.replace(/[-\s]/g, ''))) {
|
|
338
|
+
type = 'isbn';
|
|
339
|
+
} else if (/^\d+$/.test(identifier) || /pubmed|pmid/i.test(identifier)) {
|
|
340
|
+
type = 'pmid';
|
|
341
|
+
} else if (/arxiv/i.test(identifier) || /^\d{4}\.\d{4,5}/.test(identifier)) {
|
|
342
|
+
type = 'arxiv';
|
|
343
|
+
} else if (/^https?:\/\//.test(identifier)) {
|
|
344
|
+
type = 'url';
|
|
345
|
+
} else {
|
|
346
|
+
type = 'doi'; // Default
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
result = db.findItemByIdentifier(identifier, type);
|
|
347
351
|
break;
|
|
348
352
|
}
|
|
349
353
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
354
|
+
// ============================================
|
|
355
|
+
// Annotations (Consolidated)
|
|
356
|
+
// ============================================
|
|
357
|
+
case 'get_annotations': {
|
|
358
|
+
const params = getAnnotationsSchema.parse(args);
|
|
359
|
+
|
|
360
|
+
if (params.searchQuery) {
|
|
361
|
+
result = db.searchAnnotations(params.searchQuery, params.itemID);
|
|
362
|
+
} else if (params.types && params.itemID) {
|
|
363
|
+
result = db.getAnnotationsByType(params.itemID, params.types);
|
|
364
|
+
} else if (params.colors && params.itemID) {
|
|
365
|
+
result = db.getAnnotationsByColor(params.itemID, params.colors);
|
|
366
|
+
} else if (params.attachmentID) {
|
|
367
|
+
result = db.getAttachmentAnnotations(params.attachmentID);
|
|
368
|
+
} else if (params.itemID) {
|
|
369
|
+
result = db.getItemAnnotations(params.itemID);
|
|
370
|
+
} else {
|
|
371
|
+
throw new Error('At least itemID, attachmentID, or searchQuery is required');
|
|
372
|
+
}
|
|
357
373
|
break;
|
|
358
374
|
}
|
|
359
375
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
376
|
+
// ============================================
|
|
377
|
+
// Fulltext Search (Consolidated)
|
|
378
|
+
// ============================================
|
|
379
|
+
case 'search_fulltext': {
|
|
380
|
+
const params = searchFulltextSchema.parse(args);
|
|
381
|
+
|
|
382
|
+
if (params.attachmentID && !params.query) {
|
|
383
|
+
// Get fulltext content
|
|
384
|
+
result = { content: db.getFulltextContent(params.attachmentID) };
|
|
385
|
+
} else if (params.query) {
|
|
386
|
+
// Search with context
|
|
387
|
+
result = db.searchFulltextWithContext(params.query, params.contextLength, params.libraryID);
|
|
365
388
|
} else {
|
|
366
|
-
|
|
389
|
+
throw new Error('Either query or attachmentID is required');
|
|
367
390
|
}
|
|
368
391
|
break;
|
|
369
392
|
}
|
|
370
393
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
throw new Error('Could not extract PDF content');
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
const abstract = pdf.generateSimpleSummary(content, params.maxLength);
|
|
394
|
+
// ============================================
|
|
395
|
+
// Related Items (Consolidated)
|
|
396
|
+
// ============================================
|
|
397
|
+
case 'find_related_items': {
|
|
398
|
+
const params = findRelatedItemsSchema.parse(args);
|
|
380
399
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
db.
|
|
390
|
-
|
|
400
|
+
switch (params.method) {
|
|
401
|
+
case 'manual':
|
|
402
|
+
result = db.getRelatedItems(params.itemID);
|
|
403
|
+
break;
|
|
404
|
+
case 'tags':
|
|
405
|
+
result = db.findSimilarByTags(params.itemID, params.minSharedTags);
|
|
406
|
+
break;
|
|
407
|
+
case 'creators':
|
|
408
|
+
result = db.findSimilarByCreators(params.itemID);
|
|
409
|
+
break;
|
|
410
|
+
case 'collection':
|
|
411
|
+
result = db.findSimilarByCollection(params.itemID);
|
|
412
|
+
break;
|
|
413
|
+
case 'all':
|
|
414
|
+
result = {
|
|
415
|
+
manual: db.getRelatedItems(params.itemID),
|
|
416
|
+
byTags: db.findSimilarByTags(params.itemID, params.minSharedTags),
|
|
417
|
+
byCreators: db.findSimilarByCreators(params.itemID),
|
|
418
|
+
byCollection: db.findSimilarByCollection(params.itemID)
|
|
419
|
+
};
|
|
420
|
+
break;
|
|
421
|
+
default:
|
|
422
|
+
throw new Error(`Unknown method: ${params.method}`);
|
|
391
423
|
}
|
|
392
|
-
|
|
393
|
-
result = { abstract, length: abstract.length };
|
|
394
424
|
break;
|
|
395
425
|
}
|
|
396
426
|
|
|
@@ -411,115 +441,49 @@ async function main() {
|
|
|
411
441
|
|
|
412
442
|
case 'raw_query': {
|
|
413
443
|
const params = rawQuerySchema.parse(args);
|
|
414
|
-
|
|
415
|
-
// Security check - only allow SELECT queries
|
|
416
444
|
if (!params.sql.trim().toUpperCase().startsWith('SELECT')) {
|
|
417
445
|
throw new Error('Only SELECT queries are allowed');
|
|
418
446
|
}
|
|
419
|
-
|
|
420
447
|
result = db.query(params.sql, params.params);
|
|
421
448
|
break;
|
|
422
449
|
}
|
|
423
450
|
|
|
424
451
|
// ============================================
|
|
425
|
-
//
|
|
426
|
-
// ============================================
|
|
427
|
-
case 'find_by_doi': {
|
|
428
|
-
const params = findByDOISchema.parse(args);
|
|
429
|
-
result = db.findItemByDOI(params.doi);
|
|
430
|
-
break;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
case 'find_by_isbn': {
|
|
434
|
-
const params = findByISBNSchema.parse(args);
|
|
435
|
-
result = db.findItemByISBN(params.isbn);
|
|
436
|
-
break;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
case 'find_by_identifier': {
|
|
440
|
-
const params = findByIdentifierSchema.parse(args);
|
|
441
|
-
result = db.findItemByIdentifier(params.identifier, params.type);
|
|
442
|
-
break;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// ============================================
|
|
446
|
-
// Annotation Tools
|
|
452
|
+
// Library Maintenance (Consolidated)
|
|
447
453
|
// ============================================
|
|
448
|
-
case '
|
|
449
|
-
const params =
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
case 'search_fulltext': {
|
|
482
|
-
const params = searchFulltextSchema.parse(args);
|
|
483
|
-
result = db.searchFulltext(params.query, params.libraryID);
|
|
484
|
-
break;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
case 'get_fulltext_content': {
|
|
488
|
-
const params = getFulltextContentSchema.parse(args);
|
|
489
|
-
result = { content: db.getFulltextContent(params.attachmentID) };
|
|
490
|
-
break;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
case 'search_fulltext_with_context': {
|
|
494
|
-
const params = searchFulltextWithContextSchema.parse(args);
|
|
495
|
-
result = db.searchFulltextWithContext(params.query, params.contextLength, params.libraryID);
|
|
496
|
-
break;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// ============================================
|
|
500
|
-
// Related/Similar Items Tools
|
|
501
|
-
// ============================================
|
|
502
|
-
case 'get_related_items': {
|
|
503
|
-
const params = getRelatedItemsSchema.parse(args);
|
|
504
|
-
result = db.getRelatedItems(params.itemID);
|
|
505
|
-
break;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
case 'find_similar_by_tags': {
|
|
509
|
-
const params = findSimilarByTagsSchema.parse(args);
|
|
510
|
-
result = db.findSimilarByTags(params.itemID, params.minSharedTags);
|
|
511
|
-
break;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
case 'find_similar_by_creators': {
|
|
515
|
-
const params = findSimilarByCreatorsSchema.parse(args);
|
|
516
|
-
result = db.findSimilarByCreators(params.itemID);
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
case 'find_similar_by_collection': {
|
|
521
|
-
const params = findSimilarByCollectionSchema.parse(args);
|
|
522
|
-
result = db.findSimilarByCollection(params.itemID);
|
|
454
|
+
case 'library_maintenance': {
|
|
455
|
+
const params = libraryMaintenanceSchema.parse(args);
|
|
456
|
+
|
|
457
|
+
switch (params.action) {
|
|
458
|
+
case 'find_duplicates':
|
|
459
|
+
result = db.findDuplicates(params.duplicateField, params.libraryID);
|
|
460
|
+
break;
|
|
461
|
+
case 'validate_attachments':
|
|
462
|
+
result = db.validateAttachments(params.itemID, params.checkAll);
|
|
463
|
+
break;
|
|
464
|
+
case 'get_valid_attachment':
|
|
465
|
+
if (!params.parentItemID) throw new Error('parentItemID is required');
|
|
466
|
+
result = db.getValidAttachment(params.parentItemID, params.contentType);
|
|
467
|
+
break;
|
|
468
|
+
case 'find_with_valid_pdf':
|
|
469
|
+
result = db.findItemsWithValidPDF({
|
|
470
|
+
title: params.title,
|
|
471
|
+
doi: params.doi,
|
|
472
|
+
requireValidPDF: params.requireValidPDF
|
|
473
|
+
});
|
|
474
|
+
break;
|
|
475
|
+
case 'cleanup_orphans':
|
|
476
|
+
result = db.deleteOrphanAttachments(params.dryRun);
|
|
477
|
+
break;
|
|
478
|
+
case 'merge_items':
|
|
479
|
+
if (!params.targetItemID || !params.sourceItemIDs) {
|
|
480
|
+
throw new Error('targetItemID and sourceItemIDs are required');
|
|
481
|
+
}
|
|
482
|
+
result = db.mergeItems(params.targetItemID, params.sourceItemIDs);
|
|
483
|
+
break;
|
|
484
|
+
default:
|
|
485
|
+
throw new Error(`Unknown action: ${params.action}`);
|
|
486
|
+
}
|
|
523
487
|
break;
|
|
524
488
|
}
|
|
525
489
|
|
|
@@ -593,21 +557,19 @@ function zodToJsonSchema(zodType: z.ZodTypeAny): Record<string, any> {
|
|
|
593
557
|
return { type: 'boolean' };
|
|
594
558
|
}
|
|
595
559
|
|
|
596
|
-
// Handle
|
|
560
|
+
// Handle enum types
|
|
561
|
+
if (zodType instanceof z.ZodEnum) {
|
|
562
|
+
return { type: 'string', enum: zodType._def.values };
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Handle array types
|
|
597
566
|
if (zodType instanceof z.ZodArray) {
|
|
598
567
|
const elementType = zodType._def.type;
|
|
599
568
|
const itemSchema = zodToJsonSchema(elementType);
|
|
600
|
-
// If items schema is empty (from ZodAny), use a more explicit schema
|
|
601
569
|
if (Object.keys(itemSchema).length === 0) {
|
|
602
|
-
return {
|
|
603
|
-
type: 'array',
|
|
604
|
-
items: { type: 'string' } // Default to string for any type to ensure valid JSON Schema
|
|
605
|
-
};
|
|
570
|
+
return { type: 'array', items: { type: 'string' } };
|
|
606
571
|
}
|
|
607
|
-
return {
|
|
608
|
-
type: 'array',
|
|
609
|
-
items: itemSchema
|
|
610
|
-
};
|
|
572
|
+
return { type: 'array', items: itemSchema };
|
|
611
573
|
}
|
|
612
574
|
|
|
613
575
|
// Handle object types
|
|
@@ -615,7 +577,7 @@ function zodToJsonSchema(zodType: z.ZodTypeAny): Record<string, any> {
|
|
|
615
577
|
return { type: 'object' };
|
|
616
578
|
}
|
|
617
579
|
|
|
618
|
-
// Handle ZodAny
|
|
580
|
+
// Handle ZodAny
|
|
619
581
|
if (zodType instanceof z.ZodAny) {
|
|
620
582
|
return {};
|
|
621
583
|
}
|