@baruchiro/paperless-mcp 0.5.1 → 2.0.0
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 +334 -11
- package/build/api/PaperlessAPI.d.ts +29 -2
- package/build/api/PaperlessAPI.js +97 -8
- package/build/api/types.d.ts +47 -0
- package/build/index.js +28 -5
- package/build/resources/documents.d.ts +7 -0
- package/build/resources/documents.js +119 -0
- package/build/resources/documents.test.d.ts +1 -0
- package/build/resources/documents.test.js +124 -0
- package/build/server.d.ts +14 -1
- package/build/server.js +21 -5
- package/build/server.test.d.ts +1 -0
- package/build/server.test.js +54 -0
- package/build/tools/documents.d.ts +3 -1
- package/build/tools/documents.js +170 -67
- package/build/tools/documents.test.js +404 -0
- package/build/tools/mail.d.ts +3 -0
- package/build/tools/mail.js +187 -0
- package/build/tools/mail.test.d.ts +1 -0
- package/build/tools/mail.test.js +212 -0
- package/build/tools/notes.d.ts +3 -0
- package/build/tools/notes.js +55 -0
- package/build/tools/notes.test.d.ts +1 -0
- package/build/tools/notes.test.js +177 -0
- package/build/tools/utils/descriptions.d.ts +1 -1
- package/build/tools/utils/descriptions.js +1 -1
- package/build/tools/utils/documentQuery.d.ts +71 -0
- package/build/tools/utils/documentQuery.js +270 -0
- package/build/tools/utils/resourceUri.d.ts +12 -7
- package/build/tools/utils/resourceUri.js +13 -8
- package/build/tools/utils/resourceUri.test.js +12 -12
- package/build/tools/utils/selectFields.d.ts +11 -0
- package/build/tools/utils/selectFields.js +91 -0
- package/build/tools/utils/selectFields.test.d.ts +1 -0
- package/build/tools/utils/selectFields.test.js +164 -0
- package/package.json +2 -1
- package/paperless-mcp.dxt +0 -0
package/build/tools/documents.js
CHANGED
|
@@ -20,15 +20,59 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
20
20
|
return t;
|
|
21
21
|
};
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.validateFilePath = validateFilePath;
|
|
23
24
|
exports.buildBulkEditParameters = buildBulkEditParameters;
|
|
24
25
|
exports.registerDocumentTools = registerDocumentTools;
|
|
25
26
|
const zod_1 = require("zod");
|
|
27
|
+
const promises_1 = require("fs/promises");
|
|
28
|
+
const path_1 = require("path");
|
|
26
29
|
const documentEnhancer_1 = require("../api/documentEnhancer");
|
|
27
30
|
const empty_1 = require("./utils/empty");
|
|
31
|
+
const documentQuery_1 = require("./utils/documentQuery");
|
|
28
32
|
const middlewares_1 = require("./utils/middlewares");
|
|
29
33
|
const monetary_1 = require("./utils/monetary");
|
|
34
|
+
const selectFields_1 = require("./utils/selectFields");
|
|
30
35
|
const descriptions_1 = require("./utils/descriptions");
|
|
31
36
|
const resourceUri_1 = require("./utils/resourceUri");
|
|
37
|
+
const MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024;
|
|
38
|
+
const ALLOWED_UPLOAD_PATHS = process.env.PAPERLESS_MCP_UPLOAD_PATHS
|
|
39
|
+
? process.env.PAPERLESS_MCP_UPLOAD_PATHS.split(":")
|
|
40
|
+
: [];
|
|
41
|
+
/** Validates that a file path is safe to read for document upload. */
|
|
42
|
+
function validateFilePath(filePath) {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
if (!(0, path_1.isAbsolute)(filePath)) {
|
|
45
|
+
throw new Error("file_path must be an absolute path");
|
|
46
|
+
}
|
|
47
|
+
// Resolve symlinks to get canonical path for allowlist checks
|
|
48
|
+
let realPath;
|
|
49
|
+
try {
|
|
50
|
+
realPath = yield (0, promises_1.realpath)(filePath);
|
|
51
|
+
}
|
|
52
|
+
catch (_a) {
|
|
53
|
+
throw new Error("File not found");
|
|
54
|
+
}
|
|
55
|
+
if (ALLOWED_UPLOAD_PATHS.length > 0) {
|
|
56
|
+
const isAllowed = ALLOWED_UPLOAD_PATHS.some((allowedPath) => {
|
|
57
|
+
return realPath.startsWith(allowedPath + "/") || realPath === allowedPath;
|
|
58
|
+
});
|
|
59
|
+
if (!isAllowed) {
|
|
60
|
+
throw new Error("file_path is outside allowed upload directories. " +
|
|
61
|
+
"Configure PAPERLESS_MCP_UPLOAD_PATHS environment variable to specify allowed paths.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const stats = yield (0, promises_1.stat)(realPath);
|
|
65
|
+
if (!stats.isFile()) {
|
|
66
|
+
throw new Error("Path must point to a regular file");
|
|
67
|
+
}
|
|
68
|
+
if (stats.size > MAX_FILE_SIZE_BYTES) {
|
|
69
|
+
throw new Error(`File size (${Math.round(stats.size / 1024 / 1024)}MB) exceeds maximum allowed size (${MAX_FILE_SIZE_BYTES / 1024 / 1024}MB)`);
|
|
70
|
+
}
|
|
71
|
+
if (stats.size === 0) {
|
|
72
|
+
throw new Error("File is empty");
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
32
76
|
/**
|
|
33
77
|
* Builds Paperless-NGX bulk edit parameters from base parameters plus optional
|
|
34
78
|
* custom field updates.
|
|
@@ -52,8 +96,9 @@ const resourceUri_1 = require("./utils/resourceUri");
|
|
|
52
96
|
* @returns The merged API parameters with custom field updates transformed into
|
|
53
97
|
* Paperless-NGX's `add_custom_fields` record shape.
|
|
54
98
|
*/
|
|
55
|
-
function buildBulkEditParameters(parameters, addCustomFields, includeCustomFieldDefaults = false) {
|
|
56
|
-
var _a, _b;
|
|
99
|
+
function buildBulkEditParameters(parameters, addCustomFields, includeCustomFieldDefaults = false, includeTagDefaults = false) {
|
|
100
|
+
var _a, _b, _c, _d;
|
|
101
|
+
var _e, _f;
|
|
57
102
|
const apiParameters = Object.assign({}, parameters);
|
|
58
103
|
if (addCustomFields) {
|
|
59
104
|
apiParameters.add_custom_fields = Object.fromEntries(addCustomFields.map((customField) => [
|
|
@@ -65,8 +110,18 @@ function buildBulkEditParameters(parameters, addCustomFields, includeCustomField
|
|
|
65
110
|
(_a = apiParameters.add_custom_fields) !== null && _a !== void 0 ? _a : (apiParameters.add_custom_fields = {});
|
|
66
111
|
(_b = apiParameters.remove_custom_fields) !== null && _b !== void 0 ? _b : (apiParameters.remove_custom_fields = []);
|
|
67
112
|
}
|
|
113
|
+
if (includeTagDefaults) {
|
|
114
|
+
(_c = (_e = apiParameters).add_tags) !== null && _c !== void 0 ? _c : (_e.add_tags = []);
|
|
115
|
+
(_d = (_f = apiParameters).remove_tags) !== null && _d !== void 0 ? _d : (_f.remove_tags = []);
|
|
116
|
+
}
|
|
68
117
|
return apiParameters;
|
|
69
118
|
}
|
|
119
|
+
function executeDocumentQuery(api, args) {
|
|
120
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
121
|
+
const docsResponse = yield api.getDocuments((0, documentQuery_1.buildDocumentQueryString)(args));
|
|
122
|
+
return (0, documentEnhancer_1.convertDocsWithNames)(docsResponse, api);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
70
125
|
function registerDocumentTools(server, api) {
|
|
71
126
|
server.tool("bulk_edit_documents", "Perform bulk operations on multiple documents. Note: 'remove_tag' removes a tag from specific documents (tag remains in system), while 'delete_tag' permanently deletes a tag from the entire system. ⚠️ WARNING: 'delete' method permanently deletes documents and requires confirmation.", {
|
|
72
127
|
documents: zod_1.z.array(zod_1.z.number()),
|
|
@@ -144,9 +199,10 @@ function registerDocumentTools(server, api) {
|
|
|
144
199
|
}
|
|
145
200
|
const { documents, method, add_custom_fields, confirm } = args, parameters = __rest(args, ["documents", "method", "add_custom_fields", "confirm"]);
|
|
146
201
|
(0, monetary_1.validateCustomFields)(add_custom_fields);
|
|
202
|
+
const resolvedCustomFields = yield (0, selectFields_1.resolveSelectCustomFieldValues)(api, add_custom_fields, "stored");
|
|
147
203
|
const response = yield api.bulkEditDocuments(documents, method, method === "delete"
|
|
148
204
|
? {}
|
|
149
|
-
: buildBulkEditParameters(parameters,
|
|
205
|
+
: buildBulkEditParameters(parameters, resolvedCustomFields, method === "modify_custom_fields", method === "modify_tags"));
|
|
150
206
|
return {
|
|
151
207
|
content: [
|
|
152
208
|
{
|
|
@@ -156,9 +212,10 @@ function registerDocumentTools(server, api) {
|
|
|
156
212
|
],
|
|
157
213
|
};
|
|
158
214
|
})));
|
|
159
|
-
|
|
160
|
-
file: zod_1.z.string(),
|
|
161
|
-
|
|
215
|
+
const postDocumentBaseSchema = zod_1.z.object({
|
|
216
|
+
file: zod_1.z.string().optional().describe("Base64-encoded file content. Either 'file' or 'file_path' must be provided."),
|
|
217
|
+
file_path: zod_1.z.string().optional().describe("Absolute path to a file on the server's filesystem. Either 'file' or 'file_path' must be provided. The filename is derived from the path unless 'filename' is also specified. For security, configure PAPERLESS_MCP_UPLOAD_PATHS to restrict allowed directories."),
|
|
218
|
+
filename: zod_1.z.string().optional().describe("Filename for the uploaded document. Required when using 'file', optional when using 'file_path' (defaults to the basename of the path)."),
|
|
162
219
|
title: zod_1.z.string().optional(),
|
|
163
220
|
created: zod_1.z.string().optional(),
|
|
164
221
|
correspondent: zod_1.z.number().optional(),
|
|
@@ -167,16 +224,90 @@ function registerDocumentTools(server, api) {
|
|
|
167
224
|
tags: zod_1.z.array(zod_1.z.number()).optional(),
|
|
168
225
|
archive_serial_number: zod_1.z.number().optional(),
|
|
169
226
|
custom_fields: zod_1.z.array(zod_1.z.number()).optional(),
|
|
170
|
-
}
|
|
227
|
+
});
|
|
228
|
+
const postDocumentSchema = postDocumentBaseSchema.superRefine((data, ctx) => {
|
|
229
|
+
const hasFile = data.file !== undefined;
|
|
230
|
+
const hasFilePath = data.file_path !== undefined;
|
|
231
|
+
if (!hasFile && !hasFilePath) {
|
|
232
|
+
ctx.addIssue({
|
|
233
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
234
|
+
message: "Either 'file' (base64) or 'file_path' must be provided.",
|
|
235
|
+
path: ["file"],
|
|
236
|
+
});
|
|
237
|
+
ctx.addIssue({
|
|
238
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
239
|
+
message: "Either 'file' (base64) or 'file_path' must be provided.",
|
|
240
|
+
path: ["file_path"],
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (hasFile && hasFilePath) {
|
|
244
|
+
ctx.addIssue({
|
|
245
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
246
|
+
message: "Only one of 'file' or 'file_path' should be provided, not both.",
|
|
247
|
+
path: ["file"],
|
|
248
|
+
});
|
|
249
|
+
ctx.addIssue({
|
|
250
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
251
|
+
message: "Only one of 'file' or 'file_path' should be provided, not both.",
|
|
252
|
+
path: ["file_path"],
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (hasFile && !data.filename) {
|
|
256
|
+
ctx.addIssue({
|
|
257
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
258
|
+
message: "'filename' is required when using 'file' (base64 mode).",
|
|
259
|
+
path: ["filename"],
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (hasFilePath && data.file_path && !(0, path_1.isAbsolute)(data.file_path)) {
|
|
263
|
+
ctx.addIssue({
|
|
264
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
265
|
+
message: "file_path must be an absolute path",
|
|
266
|
+
path: ["file_path"],
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
server.tool("post_document", "Upload a new document to Paperless-NGX with optional metadata like title, correspondent, document type, tags, and custom fields. Provide either 'file' (base64-encoded content) or 'file_path' (absolute path to a file on the server's filesystem). Using file_path avoids base64 encoding overhead for large files. SECURITY: When using file_path, set PAPERLESS_MCP_UPLOAD_PATHS environment variable to restrict uploads to specific directories (colon-separated paths).", postDocumentBaseSchema.shape, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
171
271
|
if (!api)
|
|
172
272
|
throw new Error("Please configure API connection first");
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
273
|
+
const validationResult = postDocumentSchema.safeParse(args);
|
|
274
|
+
if (!validationResult.success) {
|
|
275
|
+
throw new Error(validationResult.error.errors.map(e => e.message).join("; "));
|
|
276
|
+
}
|
|
277
|
+
let document;
|
|
278
|
+
let filename;
|
|
279
|
+
if (args.file_path) {
|
|
280
|
+
yield validateFilePath(args.file_path);
|
|
281
|
+
try {
|
|
282
|
+
document = yield (0, promises_1.readFile)(args.file_path);
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
throw new Error("Failed to read file");
|
|
286
|
+
}
|
|
287
|
+
filename = args.filename || (0, path_1.basename)(args.file_path);
|
|
288
|
+
if (!filename) {
|
|
289
|
+
throw new Error("Could not derive filename from file_path");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else if (args.file) {
|
|
293
|
+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
294
|
+
if (!base64Regex.test(args.file)) {
|
|
295
|
+
throw new Error("Invalid base64-encoded file data. Please provide a valid base64 string.");
|
|
296
|
+
}
|
|
297
|
+
document = Buffer.from(args.file, "base64");
|
|
298
|
+
if (document.length > MAX_FILE_SIZE_BYTES) {
|
|
299
|
+
throw new Error(`File size (${Math.round(document.length / 1024 / 1024)}MB) exceeds maximum allowed size (${MAX_FILE_SIZE_BYTES / 1024 / 1024}MB)`);
|
|
300
|
+
}
|
|
301
|
+
if (document.length === 0) {
|
|
302
|
+
throw new Error("File is empty");
|
|
303
|
+
}
|
|
304
|
+
filename = args.filename;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
// This should never happen due to schema validation, but TypeScript needs it
|
|
308
|
+
throw new Error("Either 'file' (base64) or 'file_path' must be provided.");
|
|
177
309
|
}
|
|
178
|
-
const { file, filename } = args, metadata = __rest(args, ["file", "filename"]);
|
|
179
|
-
const document = Buffer.from(file, "base64");
|
|
310
|
+
const { file, file_path, filename: _fn } = args, metadata = __rest(args, ["file", "file_path", "filename"]);
|
|
180
311
|
const response = yield api.postDocument(document, filename, metadata);
|
|
181
312
|
let result;
|
|
182
313
|
if (typeof response === "string" && /^\d+$/.test(response)) {
|
|
@@ -194,43 +325,15 @@ function registerDocumentTools(server, api) {
|
|
|
194
325
|
],
|
|
195
326
|
};
|
|
196
327
|
})));
|
|
197
|
-
server.tool("list_documents", "List and filter documents
|
|
198
|
-
page: zod_1.z.number().optional(),
|
|
199
|
-
page_size: zod_1.z.number().optional(),
|
|
200
|
-
search: zod_1.z.string().optional(),
|
|
201
|
-
correspondent: zod_1.z.number().optional(),
|
|
202
|
-
document_type: zod_1.z.number().optional(),
|
|
203
|
-
tag: zod_1.z.number().optional(),
|
|
204
|
-
storage_path: zod_1.z.number().optional(),
|
|
205
|
-
created__date__gte: zod_1.z.string().optional(),
|
|
206
|
-
created__date__lte: zod_1.z.string().optional(),
|
|
207
|
-
ordering: zod_1.z.string().optional(),
|
|
208
|
-
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
328
|
+
server.tool("list_documents", "List and filter documents with pagination and common Paperless filters such as title search, correspondent, document type, tag, storage path, creation date, archive serial number, and simple custom field filters. Use 'query_documents' for full-text query, structured custom field conditions, or advanced documented /api/documents/ query parameters. IMPORTANT: For queries like 'the last 3 contributions' or when searching by tag, correspondent, document type, or storage path, first use the relevant lookup tool to find the correct ID. Note: Document content is excluded from results by default. Use 'get_document_content' when you need the document text.", documentQuery_1.LIST_DOCUMENTS_ARGS_SHAPE, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
209
329
|
if (!api)
|
|
210
330
|
throw new Error("Please configure API connection first");
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
query.set("search", args.search);
|
|
218
|
-
if (args.correspondent)
|
|
219
|
-
query.set("correspondent__id", args.correspondent.toString());
|
|
220
|
-
if (args.document_type)
|
|
221
|
-
query.set("document_type__id", args.document_type.toString());
|
|
222
|
-
if (args.tag)
|
|
223
|
-
query.set("tags__id", args.tag.toString());
|
|
224
|
-
if (args.storage_path)
|
|
225
|
-
query.set("storage_path__id", args.storage_path.toString());
|
|
226
|
-
if (args.created__date__gte)
|
|
227
|
-
query.set("created__date__gte", args.created__date__gte);
|
|
228
|
-
if (args.created__date__lte)
|
|
229
|
-
query.set("created__date__lte", args.created__date__lte);
|
|
230
|
-
if (args.ordering)
|
|
231
|
-
query.set("ordering", args.ordering);
|
|
232
|
-
const docsResponse = yield api.getDocuments(query.toString() ? `?${query.toString()}` : "");
|
|
233
|
-
return (0, documentEnhancer_1.convertDocsWithNames)(docsResponse, api);
|
|
331
|
+
return executeDocumentQuery(api, args);
|
|
332
|
+
})));
|
|
333
|
+
server.tool("query_documents", "Query documents using the full-text query engine plus structured Paperless filters. Use this for complex filtering, custom field conditions, or any documented /api/documents/ query parameters that are not exposed as first-class arguments. Prefer the dedicated top-level arguments where available. custom_field_query supports [field_name_or_id, operator, value] leaves or ['AND'|'OR', [clause1, clause2]] groups. Note: Document content is excluded from results by default. Use 'get_document_content' when you need the document text.", documentQuery_1.QUERY_DOCUMENTS_ARGS_SHAPE, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
334
|
+
if (!api)
|
|
335
|
+
throw new Error("Please configure API connection first");
|
|
336
|
+
return executeDocumentQuery(api, args);
|
|
234
337
|
})));
|
|
235
338
|
server.tool("get_document", "Get a specific document by ID with full details including correspondent, document type, tags, and custom fields. Note: Document content is excluded from results by default. Use 'get_document_content' to retrieve content when needed.", {
|
|
236
339
|
id: zod_1.z.number(),
|
|
@@ -259,51 +362,50 @@ function registerDocumentTools(server, api) {
|
|
|
259
362
|
],
|
|
260
363
|
};
|
|
261
364
|
})));
|
|
262
|
-
server.tool("search_documents", "
|
|
263
|
-
query: zod_1.z.string(),
|
|
264
|
-
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
365
|
+
server.tool("search_documents", "Deprecated compatibility wrapper for full-text document search. Use 'query_documents' with the 'query' argument for new integrations. Note: Document content is excluded from results by default. Use 'get_document_content' to retrieve content when needed.", documentQuery_1.SEARCH_DOCUMENTS_ARGS_SHAPE, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
265
366
|
if (!api)
|
|
266
367
|
throw new Error("Please configure API connection first");
|
|
267
|
-
|
|
268
|
-
return (0, documentEnhancer_1.convertDocsWithNames)(docsResponse, api);
|
|
368
|
+
return executeDocumentQuery(api, args);
|
|
269
369
|
})));
|
|
270
|
-
server.tool("download_document", "Download a document file by ID. Returns the
|
|
271
|
-
id: zod_1.z.number(),
|
|
370
|
+
server.tool("download_document", "Download a document file by ID. Returns a paperless:// resource URI; read the resource to fetch the file content.", {
|
|
371
|
+
id: zod_1.z.number().int().positive(),
|
|
272
372
|
original: zod_1.z.boolean().optional(),
|
|
273
373
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
274
|
-
var _a, _b;
|
|
275
374
|
if (!api)
|
|
276
375
|
throw new Error("Please configure API connection first");
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
: response.headers["content-disposition"])) === null || _a === void 0 ? void 0 : _a.split("filename=")[1]) === null || _b === void 0 ? void 0 : _b.replace(/"/g, "")) || `document-${args.id}`;
|
|
376
|
+
const uri = (0, resourceUri_1.buildDocumentResourceUri)(args.id, {
|
|
377
|
+
original: args.original,
|
|
378
|
+
});
|
|
281
379
|
return {
|
|
282
380
|
content: [
|
|
283
381
|
{
|
|
284
382
|
type: "resource",
|
|
285
383
|
resource: {
|
|
286
|
-
uri
|
|
287
|
-
|
|
288
|
-
|
|
384
|
+
uri,
|
|
385
|
+
// MCP SDK 1.11 embedded resources require text or blob. Keep the
|
|
386
|
+
// existing resource-shaped tool result while making resources/read
|
|
387
|
+
// the canonical place for the large binary payload.
|
|
388
|
+
text: "",
|
|
389
|
+
mimeType: "application/octet-stream",
|
|
289
390
|
},
|
|
290
391
|
},
|
|
291
392
|
],
|
|
292
393
|
};
|
|
293
394
|
})));
|
|
294
|
-
server.tool("get_document_thumbnail", "Get a document thumbnail (image preview) by ID. Returns the
|
|
295
|
-
id: zod_1.z.number(),
|
|
395
|
+
server.tool("get_document_thumbnail", "Get a document thumbnail (image preview) by ID. Returns a paperless:// resource URI; read the resource to fetch the image content.", {
|
|
396
|
+
id: zod_1.z.number().int().positive(),
|
|
296
397
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
297
398
|
if (!api)
|
|
298
399
|
throw new Error("Please configure API connection first");
|
|
299
|
-
const response = yield api.getThumbnail(args.id);
|
|
300
400
|
return {
|
|
301
401
|
content: [
|
|
302
402
|
{
|
|
303
403
|
type: "resource",
|
|
304
404
|
resource: {
|
|
305
405
|
uri: (0, resourceUri_1.buildThumbnailResourceUri)(args.id),
|
|
306
|
-
|
|
406
|
+
// See download_document above: the binary thumbnail is fetched
|
|
407
|
+
// lazily through resources/read instead of embedded here.
|
|
408
|
+
text: "",
|
|
307
409
|
mimeType: "image/webp",
|
|
308
410
|
},
|
|
309
411
|
},
|
|
@@ -373,6 +475,7 @@ function registerDocumentTools(server, api) {
|
|
|
373
475
|
throw new Error("Please configure API connection first");
|
|
374
476
|
const { id } = args, updateData = __rest(args, ["id"]);
|
|
375
477
|
(0, monetary_1.validateCustomFields)(updateData.custom_fields);
|
|
478
|
+
updateData.custom_fields = yield (0, selectFields_1.resolveSelectCustomFieldValues)(api, updateData.custom_fields, "index");
|
|
376
479
|
const response = yield api.updateDocument(id, updateData);
|
|
377
480
|
return (0, documentEnhancer_1.convertDocsWithNames)(response, api);
|
|
378
481
|
})));
|