@baruchiro/paperless-mcp 2.0.1 → 2.2.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 +3 -1
- package/build/api/PaperlessAPI.d.ts +1 -1
- package/build/api/types.d.ts +12 -14
- package/build/tools/correspondents.js +3 -15
- package/build/tools/customFields.js +3 -3
- package/build/tools/documentTypes.js +4 -16
- package/build/tools/documents.js +50 -15
- package/build/tools/documents.test.js +176 -0
- package/build/tools/mail.js +6 -6
- package/build/tools/schemaCompat.test.d.ts +1 -0
- package/build/tools/schemaCompat.test.js +59 -0
- package/build/tools/tags.js +4 -16
- package/build/tools/utils/documentQuery.d.ts +4 -4
- package/build/tools/utils/documentQuery.js +1 -1
- package/build/tools/utils/matchingAlgorithm.d.ts +2 -0
- package/build/tools/utils/matchingAlgorithm.js +15 -0
- package/package.json +3 -3
- package/paperless-mcp.dxt +0 -0
package/README.md
CHANGED
|
@@ -252,7 +252,9 @@ Parameters:
|
|
|
252
252
|
- tag: ID for add_tag/remove_tag
|
|
253
253
|
- add_tags: Array of tag IDs for modify_tags
|
|
254
254
|
- remove_tags: Array of tag IDs for modify_tags
|
|
255
|
-
-
|
|
255
|
+
- set_permissions: Object for set_permissions with view/change users and groups (`{"view": {"users": [], "groups": []}, "change": {...}}`). Omitted actions/lists are left untouched
|
|
256
|
+
- owner: User ID (or null to remove) for set_permissions. Unless merge is true, omitting owner clears the current owner
|
|
257
|
+
- merge: Boolean for set_permissions — true adds to existing permissions and keeps the owner; false (default) replaces the listed users/groups
|
|
256
258
|
- metadata_document_id: ID for merge to specify metadata source
|
|
257
259
|
- delete_originals: Boolean for merge/split
|
|
258
260
|
- pages: String for split "[1,2-3,4,5-7]" or delete_pages "[2,3,4]"
|
|
@@ -7,7 +7,7 @@ export declare class PaperlessAPI {
|
|
|
7
7
|
constructor(baseUrl: string, token: string);
|
|
8
8
|
request<T = any>(path: string, options?: RequestInit): Promise<T>;
|
|
9
9
|
bulkEditDocuments(documents: number[], method: string, parameters?: BulkEditParameters): Promise<BulkEditDocumentsResult>;
|
|
10
|
-
postDocument(document: Buffer, filename: string, metadata?: Record<string, string | string[] | number | number[]>): Promise<string>;
|
|
10
|
+
postDocument(document: Buffer, filename: string, metadata?: Record<string, string | string[] | number | number[] | undefined>): Promise<string>;
|
|
11
11
|
getDocuments(query?: string): Promise<DocumentsResponse>;
|
|
12
12
|
getDocument(id: number): Promise<Document>;
|
|
13
13
|
updateDocument(id: number, data: Partial<Document>): Promise<Document>;
|
package/build/api/types.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ export interface Document {
|
|
|
68
68
|
id: number;
|
|
69
69
|
correspondent: number | null;
|
|
70
70
|
document_type: number | null;
|
|
71
|
-
storage_path:
|
|
71
|
+
storage_path: number | null;
|
|
72
72
|
title: string;
|
|
73
73
|
content: string | null;
|
|
74
74
|
tags: number[];
|
|
@@ -77,7 +77,7 @@ export interface Document {
|
|
|
77
77
|
modified: string;
|
|
78
78
|
added: string;
|
|
79
79
|
deleted_at: string | null;
|
|
80
|
-
archive_serial_number:
|
|
80
|
+
archive_serial_number: number | null;
|
|
81
81
|
original_file_name: string;
|
|
82
82
|
archived_file_name: string;
|
|
83
83
|
owner: number | null;
|
|
@@ -188,18 +188,16 @@ export interface BulkEditParameters {
|
|
|
188
188
|
document_type?: number;
|
|
189
189
|
storage_path?: number;
|
|
190
190
|
tag?: number;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
users: number[];
|
|
200
|
-
groups: number[];
|
|
201
|
-
};
|
|
191
|
+
set_permissions?: {
|
|
192
|
+
view?: {
|
|
193
|
+
users?: number[];
|
|
194
|
+
groups?: number[];
|
|
195
|
+
};
|
|
196
|
+
change?: {
|
|
197
|
+
users?: number[];
|
|
198
|
+
groups?: number[];
|
|
202
199
|
};
|
|
203
|
-
merge?: boolean;
|
|
204
200
|
};
|
|
201
|
+
owner?: number | null;
|
|
202
|
+
merge?: boolean;
|
|
205
203
|
}
|
|
@@ -22,8 +22,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
23
|
exports.registerCorrespondentTools = registerCorrespondentTools;
|
|
24
24
|
const zod_1 = require("zod");
|
|
25
|
-
const types_1 = require("../api/types");
|
|
26
25
|
const utils_1 = require("../api/utils");
|
|
26
|
+
const matchingAlgorithm_1 = require("./utils/matchingAlgorithm");
|
|
27
27
|
const middlewares_1 = require("./utils/middlewares");
|
|
28
28
|
const queryString_1 = require("./utils/queryString");
|
|
29
29
|
function registerCorrespondentTools(server, api) {
|
|
@@ -64,13 +64,7 @@ function registerCorrespondentTools(server, api) {
|
|
|
64
64
|
server.tool("create_correspondent", "Create a new correspondent with optional matching pattern and algorithm for automatic document assignment.", {
|
|
65
65
|
name: zod_1.z.string(),
|
|
66
66
|
match: zod_1.z.string().optional(),
|
|
67
|
-
matching_algorithm:
|
|
68
|
-
.number()
|
|
69
|
-
.int()
|
|
70
|
-
.min(0)
|
|
71
|
-
.max(6)
|
|
72
|
-
.optional()
|
|
73
|
-
.describe(types_1.MATCHING_ALGORITHM_DESCRIPTION),
|
|
67
|
+
matching_algorithm: matchingAlgorithm_1.matchingAlgorithmSchema.optional(),
|
|
74
68
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
75
69
|
if (!api)
|
|
76
70
|
throw new Error("Please configure API connection first");
|
|
@@ -86,13 +80,7 @@ function registerCorrespondentTools(server, api) {
|
|
|
86
80
|
id: zod_1.z.number(),
|
|
87
81
|
name: zod_1.z.string(),
|
|
88
82
|
match: zod_1.z.string().optional(),
|
|
89
|
-
matching_algorithm:
|
|
90
|
-
.number()
|
|
91
|
-
.int()
|
|
92
|
-
.min(0)
|
|
93
|
-
.max(6)
|
|
94
|
-
.optional()
|
|
95
|
-
.describe(types_1.MATCHING_ALGORITHM_DESCRIPTION),
|
|
83
|
+
matching_algorithm: matchingAlgorithm_1.matchingAlgorithmSchema.optional(),
|
|
96
84
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
97
85
|
if (!api)
|
|
98
86
|
throw new Error("Please configure API connection first");
|
|
@@ -33,7 +33,7 @@ function registerCustomFieldTools(server, api) {
|
|
|
33
33
|
name__iexact: zod_1.z.string().optional(),
|
|
34
34
|
name__istartswith: zod_1.z.string().optional(),
|
|
35
35
|
ordering: zod_1.z.string().optional(),
|
|
36
|
-
}, (0, middlewares_1.withErrorHandling)((
|
|
36
|
+
}, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
|
|
37
37
|
if (!api)
|
|
38
38
|
throw new Error("Please configure API connection first");
|
|
39
39
|
const queryString = (0, queryString_1.buildQueryString)(args);
|
|
@@ -68,7 +68,7 @@ function registerCustomFieldTools(server, api) {
|
|
|
68
68
|
"documentlink",
|
|
69
69
|
"select",
|
|
70
70
|
]),
|
|
71
|
-
extra_data: zod_1.z.record(zod_1.z.unknown()).nullable().optional(),
|
|
71
|
+
extra_data: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable().optional(),
|
|
72
72
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
73
73
|
if (!api)
|
|
74
74
|
throw new Error("Please configure API connection first");
|
|
@@ -93,7 +93,7 @@ function registerCustomFieldTools(server, api) {
|
|
|
93
93
|
"select",
|
|
94
94
|
])
|
|
95
95
|
.optional(),
|
|
96
|
-
extra_data: zod_1.z.record(zod_1.z.unknown()).nullable().optional(),
|
|
96
|
+
extra_data: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable().optional(),
|
|
97
97
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
98
98
|
if (!api)
|
|
99
99
|
throw new Error("Please configure API connection first");
|
|
@@ -22,8 +22,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
23
|
exports.registerDocumentTypeTools = registerDocumentTypeTools;
|
|
24
24
|
const zod_1 = require("zod");
|
|
25
|
-
const types_1 = require("../api/types");
|
|
26
25
|
const utils_1 = require("../api/utils");
|
|
26
|
+
const matchingAlgorithm_1 = require("./utils/matchingAlgorithm");
|
|
27
27
|
const middlewares_1 = require("./utils/middlewares");
|
|
28
28
|
const queryString_1 = require("./utils/queryString");
|
|
29
29
|
function registerDocumentTypeTools(server, api) {
|
|
@@ -35,7 +35,7 @@ function registerDocumentTypeTools(server, api) {
|
|
|
35
35
|
name__iexact: zod_1.z.string().optional(),
|
|
36
36
|
name__istartswith: zod_1.z.string().optional(),
|
|
37
37
|
ordering: zod_1.z.string().optional(),
|
|
38
|
-
}, (0, middlewares_1.withErrorHandling)((
|
|
38
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
39
39
|
if (!api)
|
|
40
40
|
throw new Error("Please configure API connection first");
|
|
41
41
|
const queryString = (0, queryString_1.buildQueryString)(args);
|
|
@@ -62,13 +62,7 @@ function registerDocumentTypeTools(server, api) {
|
|
|
62
62
|
server.tool("create_document_type", "Create a new document type with optional matching pattern and algorithm for automatic document classification.", {
|
|
63
63
|
name: zod_1.z.string(),
|
|
64
64
|
match: zod_1.z.string().optional(),
|
|
65
|
-
matching_algorithm:
|
|
66
|
-
.number()
|
|
67
|
-
.int()
|
|
68
|
-
.min(0)
|
|
69
|
-
.max(6)
|
|
70
|
-
.optional()
|
|
71
|
-
.describe(types_1.MATCHING_ALGORITHM_DESCRIPTION),
|
|
65
|
+
matching_algorithm: matchingAlgorithm_1.matchingAlgorithmSchema.optional(),
|
|
72
66
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
73
67
|
if (!api)
|
|
74
68
|
throw new Error("Please configure API connection first");
|
|
@@ -82,13 +76,7 @@ function registerDocumentTypeTools(server, api) {
|
|
|
82
76
|
id: zod_1.z.number(),
|
|
83
77
|
name: zod_1.z.string(),
|
|
84
78
|
match: zod_1.z.string().optional(),
|
|
85
|
-
matching_algorithm:
|
|
86
|
-
.number()
|
|
87
|
-
.int()
|
|
88
|
-
.min(0)
|
|
89
|
-
.max(6)
|
|
90
|
-
.optional()
|
|
91
|
-
.describe(types_1.MATCHING_ALGORITHM_DESCRIPTION),
|
|
79
|
+
matching_algorithm: matchingAlgorithm_1.matchingAlgorithmSchema.optional(),
|
|
92
80
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
93
81
|
if (!api)
|
|
94
82
|
throw new Error("Please configure API connection first");
|
package/build/tools/documents.js
CHANGED
|
@@ -164,25 +164,38 @@ function registerDocumentTools(server, api) {
|
|
|
164
164
|
.array(zod_1.z.number())
|
|
165
165
|
.optional()
|
|
166
166
|
.transform(empty_1.arrayNotEmpty),
|
|
167
|
-
|
|
167
|
+
set_permissions: zod_1.z
|
|
168
168
|
.object({
|
|
169
|
-
|
|
170
|
-
set_permissions: zod_1.z
|
|
169
|
+
view: zod_1.z
|
|
171
170
|
.object({
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
groups: zod_1.z.array(zod_1.z.number()),
|
|
175
|
-
}),
|
|
176
|
-
change: zod_1.z.object({
|
|
177
|
-
users: zod_1.z.array(zod_1.z.number()),
|
|
178
|
-
groups: zod_1.z.array(zod_1.z.number()),
|
|
179
|
-
}),
|
|
171
|
+
users: zod_1.z.array(zod_1.z.number()).optional(),
|
|
172
|
+
groups: zod_1.z.array(zod_1.z.number()).optional(),
|
|
180
173
|
})
|
|
174
|
+
.strict()
|
|
175
|
+
.optional(),
|
|
176
|
+
change: zod_1.z
|
|
177
|
+
.object({
|
|
178
|
+
users: zod_1.z.array(zod_1.z.number()).optional(),
|
|
179
|
+
groups: zod_1.z.array(zod_1.z.number()).optional(),
|
|
180
|
+
})
|
|
181
|
+
.strict()
|
|
181
182
|
.optional(),
|
|
182
|
-
merge: zod_1.z.boolean().optional(),
|
|
183
183
|
})
|
|
184
|
+
// strict: a misspelled action ("read") must not silently collapse
|
|
185
|
+
// into {} and clear permissions/ownership.
|
|
186
|
+
.strict()
|
|
187
|
+
.optional()
|
|
188
|
+
.describe("For set_permissions: view/change permissions to apply. Omitted actions (view/change) and omitted users/groups lists are left untouched; an empty list [] removes all (unless merge is true). Omit entirely for owner-only changes."),
|
|
189
|
+
owner: zod_1.z
|
|
190
|
+
.number()
|
|
191
|
+
.int()
|
|
192
|
+
.nullable()
|
|
193
|
+
.optional()
|
|
194
|
+
.describe("For set_permissions: new owner user ID, or null to remove the owner. Unless merge is true, omitting owner also clears the current owner."),
|
|
195
|
+
merge: zod_1.z
|
|
196
|
+
.boolean()
|
|
184
197
|
.optional()
|
|
185
|
-
.
|
|
198
|
+
.describe("For set_permissions: true adds to existing permissions and keeps the current owner; false (default) replaces the listed users/groups"),
|
|
186
199
|
metadata_document_id: zod_1.z.number().optional(),
|
|
187
200
|
delete_originals: zod_1.z.boolean().optional(),
|
|
188
201
|
pages: zod_1.z.string().optional(),
|
|
@@ -192,12 +205,23 @@ function registerDocumentTools(server, api) {
|
|
|
192
205
|
.optional()
|
|
193
206
|
.describe("Must be true when method is 'delete' to confirm destructive operation"),
|
|
194
207
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
208
|
+
var _a;
|
|
195
209
|
if (!api)
|
|
196
210
|
throw new Error("Please configure API connection first");
|
|
197
211
|
if (args.method === "delete" && !args.confirm) {
|
|
198
212
|
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
199
213
|
}
|
|
214
|
+
if (args.method === "set_permissions" &&
|
|
215
|
+
!args.set_permissions &&
|
|
216
|
+
args.owner === undefined) {
|
|
217
|
+
throw new Error("Method 'set_permissions' requires set_permissions and/or owner.");
|
|
218
|
+
}
|
|
200
219
|
const { documents, method, add_custom_fields, confirm } = args, parameters = __rest(args, ["documents", "method", "add_custom_fields", "confirm"]);
|
|
220
|
+
if (method === "set_permissions") {
|
|
221
|
+
// Paperless rejects (Paperless <= 3.0.5: crashes with a 500 on) a missing
|
|
222
|
+
// set_permissions key even for owner-only changes.
|
|
223
|
+
(_a = parameters.set_permissions) !== null && _a !== void 0 ? _a : (parameters.set_permissions = {});
|
|
224
|
+
}
|
|
201
225
|
(0, monetary_1.validateCustomFields)(add_custom_fields);
|
|
202
226
|
const resolvedCustomFields = yield (0, selectFields_1.resolveSelectCustomFieldValues)(api, add_custom_fields, "stored");
|
|
203
227
|
const response = yield api.bulkEditDocuments(documents, method, method === "delete"
|
|
@@ -272,7 +296,7 @@ function registerDocumentTools(server, api) {
|
|
|
272
296
|
throw new Error("Please configure API connection first");
|
|
273
297
|
const validationResult = postDocumentSchema.safeParse(args);
|
|
274
298
|
if (!validationResult.success) {
|
|
275
|
-
throw new Error(validationResult.error.
|
|
299
|
+
throw new Error(validationResult.error.issues.map(e => e.message).join("; "));
|
|
276
300
|
}
|
|
277
301
|
let document;
|
|
278
302
|
let filename;
|
|
@@ -378,6 +402,9 @@ function registerDocumentTools(server, api) {
|
|
|
378
402
|
});
|
|
379
403
|
return {
|
|
380
404
|
content: [
|
|
405
|
+
// Legacy clients surface only content[].text and drop resource blocks
|
|
406
|
+
// entirely, so the URI is repeated here to stay reachable (issue #134).
|
|
407
|
+
{ type: "text", text: uri },
|
|
381
408
|
{
|
|
382
409
|
type: "resource",
|
|
383
410
|
resource: {
|
|
@@ -397,12 +424,16 @@ function registerDocumentTools(server, api) {
|
|
|
397
424
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
398
425
|
if (!api)
|
|
399
426
|
throw new Error("Please configure API connection first");
|
|
427
|
+
const uri = (0, resourceUri_1.buildThumbnailResourceUri)(args.id);
|
|
400
428
|
return {
|
|
401
429
|
content: [
|
|
430
|
+
// See download_document above: the URI is repeated as text for legacy
|
|
431
|
+
// clients that drop resource blocks.
|
|
432
|
+
{ type: "text", text: uri },
|
|
402
433
|
{
|
|
403
434
|
type: "resource",
|
|
404
435
|
resource: {
|
|
405
|
-
uri
|
|
436
|
+
uri,
|
|
406
437
|
// See download_document above: the binary thumbnail is fetched
|
|
407
438
|
// lazily through resources/read instead of embedded here.
|
|
408
439
|
text: "",
|
|
@@ -421,16 +452,19 @@ function registerDocumentTools(server, api) {
|
|
|
421
452
|
.describe("The new title for the document (max 128 characters)"),
|
|
422
453
|
correspondent: zod_1.z
|
|
423
454
|
.number()
|
|
455
|
+
.int()
|
|
424
456
|
.nullable()
|
|
425
457
|
.optional()
|
|
426
458
|
.describe("The ID of the correspondent to assign"),
|
|
427
459
|
document_type: zod_1.z
|
|
428
460
|
.number()
|
|
461
|
+
.int()
|
|
429
462
|
.nullable()
|
|
430
463
|
.optional()
|
|
431
464
|
.describe("The ID of the document type to assign"),
|
|
432
465
|
storage_path: zod_1.z
|
|
433
466
|
.number()
|
|
467
|
+
.int()
|
|
434
468
|
.nullable()
|
|
435
469
|
.optional()
|
|
436
470
|
.describe("The ID of the storage path to assign"),
|
|
@@ -452,6 +486,7 @@ function registerDocumentTools(server, api) {
|
|
|
452
486
|
.describe("The archive serial number (0-4294967295)"),
|
|
453
487
|
owner: zod_1.z
|
|
454
488
|
.number()
|
|
489
|
+
.int()
|
|
455
490
|
.nullable()
|
|
456
491
|
.optional()
|
|
457
492
|
.describe("The ID of the user who owns the document"),
|
|
@@ -21,6 +21,7 @@ const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
|
21
21
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
22
22
|
const documents_1 = require("./documents");
|
|
23
23
|
const documentQuery_1 = require("./utils/documentQuery");
|
|
24
|
+
const resourceUri_1 = require("./utils/resourceUri");
|
|
24
25
|
function getQueryParams(queryString) {
|
|
25
26
|
return new URLSearchParams(queryString.replace(/^\?/, ""));
|
|
26
27
|
}
|
|
@@ -480,3 +481,178 @@ const OBJECT_SELECT_FIELD = {
|
|
|
480
481
|
strict_1.default.equal(calls.updateDocument.length, 0, "no document update should be sent when the option is invalid");
|
|
481
482
|
}));
|
|
482
483
|
});
|
|
484
|
+
(0, node_test_1.describe)("document resource reference tools", () => {
|
|
485
|
+
// Expected URIs come from the builders rather than literals: their exact
|
|
486
|
+
// format is already pinned by utils/resourceUri.test.ts, and what matters
|
|
487
|
+
// here is that the handler surfaces that URI in both content blocks.
|
|
488
|
+
const cases = [
|
|
489
|
+
{
|
|
490
|
+
tool: "download_document",
|
|
491
|
+
args: { id: 4 },
|
|
492
|
+
uri: (0, resourceUri_1.buildDocumentResourceUri)(4),
|
|
493
|
+
mimeType: "application/octet-stream",
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
tool: "download_document",
|
|
497
|
+
args: { id: 4, original: true },
|
|
498
|
+
uri: (0, resourceUri_1.buildDocumentResourceUri)(4, { original: true }),
|
|
499
|
+
mimeType: "application/octet-stream",
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
tool: "get_document_thumbnail",
|
|
503
|
+
args: { id: 123 },
|
|
504
|
+
uri: (0, resourceUri_1.buildThumbnailResourceUri)(123),
|
|
505
|
+
mimeType: "image/webp",
|
|
506
|
+
},
|
|
507
|
+
];
|
|
508
|
+
for (const { tool, args, uri, mimeType } of cases) {
|
|
509
|
+
(0, node_test_1.test)(`${tool} ${JSON.stringify(args)} returns the URI as text beside the resource`, () => __awaiter(void 0, void 0, void 0, function* () {
|
|
510
|
+
const { api } = createDocumentApi([]);
|
|
511
|
+
let result;
|
|
512
|
+
yield withDocumentClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
513
|
+
result = (yield client.callTool({
|
|
514
|
+
name: tool,
|
|
515
|
+
arguments: args,
|
|
516
|
+
}));
|
|
517
|
+
}));
|
|
518
|
+
strict_1.default.ok(result && !result.isError, `${tool} failed`);
|
|
519
|
+
const [text, embedded] = result.content;
|
|
520
|
+
// Legacy clients read only content[].text (issue #134).
|
|
521
|
+
strict_1.default.equal(text.type, "text");
|
|
522
|
+
strict_1.default.equal(text.text, uri);
|
|
523
|
+
strict_1.default.equal(embedded.type, "resource");
|
|
524
|
+
const { resource } = embedded;
|
|
525
|
+
strict_1.default.equal(resource.uri, uri, "both blocks must reference the same URI");
|
|
526
|
+
strict_1.default.equal(resource.mimeType, mimeType);
|
|
527
|
+
}));
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
(0, node_test_1.describe)("bulk_edit_documents set_permissions", () => {
|
|
531
|
+
(0, node_test_1.test)("sends set_permissions, owner and merge at the top level of parameters", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
532
|
+
const { api, calls } = createDocumentApi([]);
|
|
533
|
+
yield withDocumentClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
534
|
+
var _a;
|
|
535
|
+
const result = (yield client.callTool({
|
|
536
|
+
name: "bulk_edit_documents",
|
|
537
|
+
arguments: {
|
|
538
|
+
documents: [4103],
|
|
539
|
+
method: "set_permissions",
|
|
540
|
+
set_permissions: {
|
|
541
|
+
view: { users: [], groups: [10] },
|
|
542
|
+
change: { users: [], groups: [10] },
|
|
543
|
+
},
|
|
544
|
+
owner: 3,
|
|
545
|
+
merge: true,
|
|
546
|
+
},
|
|
547
|
+
}));
|
|
548
|
+
strict_1.default.ok(!result.isError, (_a = parseToolText(result)) === null || _a === void 0 ? void 0 : _a.error);
|
|
549
|
+
}));
|
|
550
|
+
strict_1.default.equal(calls.bulkEditDocuments.length, 1);
|
|
551
|
+
const [documents, method, parameters] = calls.bulkEditDocuments[0];
|
|
552
|
+
strict_1.default.deepEqual(documents, [4103]);
|
|
553
|
+
strict_1.default.equal(method, "set_permissions");
|
|
554
|
+
// Paperless reads parameters["set_permissions"] directly; nesting it under
|
|
555
|
+
// another key makes the server crash with a KeyError (HTTP 500).
|
|
556
|
+
strict_1.default.deepEqual(parameters, {
|
|
557
|
+
set_permissions: {
|
|
558
|
+
view: { users: [], groups: [10] },
|
|
559
|
+
change: { users: [], groups: [10] },
|
|
560
|
+
},
|
|
561
|
+
owner: 3,
|
|
562
|
+
merge: true,
|
|
563
|
+
});
|
|
564
|
+
}));
|
|
565
|
+
(0, node_test_1.test)("forwards partial permissions without filling in omitted actions or lists", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
566
|
+
const { api, calls } = createDocumentApi([]);
|
|
567
|
+
yield withDocumentClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
568
|
+
var _a;
|
|
569
|
+
const result = (yield client.callTool({
|
|
570
|
+
name: "bulk_edit_documents",
|
|
571
|
+
arguments: {
|
|
572
|
+
documents: [1],
|
|
573
|
+
method: "set_permissions",
|
|
574
|
+
set_permissions: { view: { groups: [10] } },
|
|
575
|
+
},
|
|
576
|
+
}));
|
|
577
|
+
strict_1.default.ok(!result.isError, (_a = parseToolText(result)) === null || _a === void 0 ? void 0 : _a.error);
|
|
578
|
+
}));
|
|
579
|
+
// Paperless only touches the actions/lists that are present, and clears
|
|
580
|
+
// the owner when it is omitted without merge — so nothing may be added.
|
|
581
|
+
const [, , parameters] = calls.bulkEditDocuments[0];
|
|
582
|
+
strict_1.default.deepEqual(parameters, { set_permissions: { view: { groups: [10] } } });
|
|
583
|
+
}));
|
|
584
|
+
(0, node_test_1.test)("owner-only changes send an empty set_permissions object", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
585
|
+
const { api, calls } = createDocumentApi([]);
|
|
586
|
+
yield withDocumentClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
587
|
+
var _a;
|
|
588
|
+
for (const owner of [3, null]) {
|
|
589
|
+
const result = (yield client.callTool({
|
|
590
|
+
name: "bulk_edit_documents",
|
|
591
|
+
arguments: { documents: [1], method: "set_permissions", owner },
|
|
592
|
+
}));
|
|
593
|
+
strict_1.default.ok(!result.isError, (_a = parseToolText(result)) === null || _a === void 0 ? void 0 : _a.error);
|
|
594
|
+
}
|
|
595
|
+
}));
|
|
596
|
+
strict_1.default.deepEqual(calls.bulkEditDocuments.map(([, , parameters]) => parameters), [
|
|
597
|
+
{ set_permissions: {}, owner: 3 },
|
|
598
|
+
{ set_permissions: {}, owner: null },
|
|
599
|
+
]);
|
|
600
|
+
}));
|
|
601
|
+
(0, node_test_1.test)("rejects method set_permissions with neither set_permissions nor owner", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
602
|
+
const { api, calls } = createDocumentApi([]);
|
|
603
|
+
yield withDocumentClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
604
|
+
var _a, _b;
|
|
605
|
+
const result = (yield client.callTool({
|
|
606
|
+
name: "bulk_edit_documents",
|
|
607
|
+
arguments: { documents: [1], method: "set_permissions", merge: true },
|
|
608
|
+
}));
|
|
609
|
+
strict_1.default.ok(result.isError, "expected an error when both are missing");
|
|
610
|
+
strict_1.default.match((_b = (_a = parseToolText(result)) === null || _a === void 0 ? void 0 : _a.error) !== null && _b !== void 0 ? _b : "", /set_permissions and\/or owner/);
|
|
611
|
+
}));
|
|
612
|
+
strict_1.default.equal(calls.bulkEditDocuments.length, 0);
|
|
613
|
+
}));
|
|
614
|
+
(0, node_test_1.test)("rejects malformed set_permissions input before calling Paperless", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
615
|
+
const { api, calls } = createDocumentApi([]);
|
|
616
|
+
yield withDocumentClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
617
|
+
for (const set_permissions of [
|
|
618
|
+
{ view: { groups: ["ai-agents"] } },
|
|
619
|
+
{ read: { groups: [10] } },
|
|
620
|
+
{ view: [10] },
|
|
621
|
+
]) {
|
|
622
|
+
// Depending on the installed MCP SDK version, an input-schema (zod)
|
|
623
|
+
// violation either rejects with a protocol error (-32602) or resolves
|
|
624
|
+
// with a CallToolResult carrying isError: true.
|
|
625
|
+
let rejected = false;
|
|
626
|
+
let result;
|
|
627
|
+
try {
|
|
628
|
+
result = (yield client.callTool({
|
|
629
|
+
name: "bulk_edit_documents",
|
|
630
|
+
arguments: { documents: [1], method: "set_permissions", set_permissions },
|
|
631
|
+
}));
|
|
632
|
+
}
|
|
633
|
+
catch (_a) {
|
|
634
|
+
rejected = true;
|
|
635
|
+
}
|
|
636
|
+
strict_1.default.ok(rejected || (result === null || result === void 0 ? void 0 : result.isError), `expected ${JSON.stringify(set_permissions)} to be rejected`);
|
|
637
|
+
}
|
|
638
|
+
}));
|
|
639
|
+
strict_1.default.equal(calls.bulkEditDocuments.length, 0);
|
|
640
|
+
}));
|
|
641
|
+
});
|
|
642
|
+
(0, node_test_1.describe)("nullable foreign keys can still be cleared (#138)", () => {
|
|
643
|
+
(0, node_test_1.test)("update_document forwards an explicit null so a foreign key can be cleared", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
644
|
+
const { api, calls } = createDocumentApi([]);
|
|
645
|
+
yield withDocumentClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
646
|
+
var _a;
|
|
647
|
+
const result = (yield client.callTool({
|
|
648
|
+
name: "update_document",
|
|
649
|
+
arguments: { id: 42, correspondent: null, owner: null },
|
|
650
|
+
}));
|
|
651
|
+
strict_1.default.ok(!result.isError, (_a = parseToolText(result)) === null || _a === void 0 ? void 0 : _a.error);
|
|
652
|
+
}));
|
|
653
|
+
strict_1.default.equal(calls.updateDocument.length, 1);
|
|
654
|
+
const [, data] = calls.updateDocument[0];
|
|
655
|
+
strict_1.default.equal(data.correspondent, null);
|
|
656
|
+
strict_1.default.equal(data.owner, null);
|
|
657
|
+
}));
|
|
658
|
+
});
|
package/build/tools/mail.js
CHANGED
|
@@ -35,10 +35,10 @@ const mailRuleFields = {
|
|
|
35
35
|
account: zod_1.z.number().int().optional(),
|
|
36
36
|
enabled: zod_1.z.boolean().optional(),
|
|
37
37
|
folder: zod_1.z.string().optional(),
|
|
38
|
-
filter_from: zod_1.z.string().nullable().optional(),
|
|
39
|
-
filter_to: zod_1.z.string().nullable().optional(),
|
|
40
|
-
filter_subject: zod_1.z.string().nullable().optional(),
|
|
41
|
-
filter_body: zod_1.z.string().nullable().optional(),
|
|
38
|
+
filter_from: zod_1.z.string().max(256).nullable().optional(),
|
|
39
|
+
filter_to: zod_1.z.string().max(256).nullable().optional(),
|
|
40
|
+
filter_subject: zod_1.z.string().max(256).nullable().optional(),
|
|
41
|
+
filter_body: zod_1.z.string().max(256).nullable().optional(),
|
|
42
42
|
filter_attachment_filename_include: zod_1.z.string().nullable().optional(),
|
|
43
43
|
filter_attachment_filename_exclude: zod_1.z.string().nullable().optional(),
|
|
44
44
|
maximum_age: zod_1.z.number().int().min(0).optional(),
|
|
@@ -96,7 +96,7 @@ function registerMailTools(server, api) {
|
|
|
96
96
|
server.tool("list_mail_accounts", "List Paperless mail accounts for selecting the account ID needed by mail rules. Does not expose account passwords.", {
|
|
97
97
|
page: zod_1.z.number().optional(),
|
|
98
98
|
page_size: zod_1.z.number().optional(),
|
|
99
|
-
}, (0, middlewares_1.withErrorHandling)((
|
|
99
|
+
}, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
|
|
100
100
|
if (!api)
|
|
101
101
|
throw new Error("Please configure API connection first");
|
|
102
102
|
const queryString = (0, queryString_1.buildQueryString)(args);
|
|
@@ -132,7 +132,7 @@ function registerMailTools(server, api) {
|
|
|
132
132
|
server.tool("list_mail_rules", "List Paperless mail rules with optional pagination.", {
|
|
133
133
|
page: zod_1.z.number().optional(),
|
|
134
134
|
page_size: zod_1.z.number().optional(),
|
|
135
|
-
}, (0, middlewares_1.withErrorHandling)((
|
|
135
|
+
}, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
|
|
136
136
|
if (!api)
|
|
137
137
|
throw new Error("Please configure API connection first");
|
|
138
138
|
const queryString = (0, queryString_1.buildQueryString)(args);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
16
|
+
const node_test_1 = require("node:test");
|
|
17
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
18
|
+
const inMemory_js_1 = require("@modelcontextprotocol/sdk/inMemory.js");
|
|
19
|
+
const server_1 = require("../server");
|
|
20
|
+
// Strict MCP clients and gateways reject an array-valued `type` and silently drop
|
|
21
|
+
// the whole tool (#138). A JSON Schema converter reaches that form by collapsing a
|
|
22
|
+
// union of unchecked primitives: `zod-to-json-schema` (the zod v3 path) always does
|
|
23
|
+
// it, and zod's own converter did it again from 4.5.0 on. The tools are only free
|
|
24
|
+
// of it because the server is on the zod v4 path with zod pinned to 4.4.x, so this
|
|
25
|
+
// walks every advertised schema rather than a field list: bumping that pin, moving
|
|
26
|
+
// off zod v4, or adding a construct that reintroduces the form fails here.
|
|
27
|
+
function arrayFormTypes(node, path = "") {
|
|
28
|
+
if (Array.isArray(node)) {
|
|
29
|
+
return node.flatMap((item, i) => arrayFormTypes(item, `${path}[${i}]`));
|
|
30
|
+
}
|
|
31
|
+
if (!node || typeof node !== "object") {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
return Object.entries(node).flatMap(([key, value]) => key === "type" && Array.isArray(value)
|
|
35
|
+
? [`${path}.type = ${JSON.stringify(value)}`]
|
|
36
|
+
: arrayFormTypes(value, `${path}.${key}`));
|
|
37
|
+
}
|
|
38
|
+
(0, node_test_1.test)("no tool advertises an array-form `type` (#138)", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
+
const server = (0, server_1.createMcpServer)({
|
|
40
|
+
baseUrl: "http://paperless.test",
|
|
41
|
+
token: "test-token",
|
|
42
|
+
version: "0.0.0-test",
|
|
43
|
+
publicUrl: "http://paperless.test",
|
|
44
|
+
});
|
|
45
|
+
const [clientTransport, serverTransport] = inMemory_js_1.InMemoryTransport.createLinkedPair();
|
|
46
|
+
const client = new index_js_1.Client({ name: "schema-compat-test", version: "1.0.0" });
|
|
47
|
+
yield Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
|
|
48
|
+
try {
|
|
49
|
+
const { tools } = yield client.listTools();
|
|
50
|
+
const found = tools
|
|
51
|
+
.flatMap((tool) => arrayFormTypes(tool.inputSchema).map((hit) => `${tool.name} ${hit}`))
|
|
52
|
+
.sort();
|
|
53
|
+
strict_1.default.deepEqual(found, []);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
yield client.close();
|
|
57
|
+
yield server.close();
|
|
58
|
+
}
|
|
59
|
+
}));
|
package/build/tools/tags.js
CHANGED
|
@@ -11,8 +11,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.registerTagTools = registerTagTools;
|
|
13
13
|
const zod_1 = require("zod");
|
|
14
|
-
const types_1 = require("../api/types");
|
|
15
14
|
const utils_1 = require("../api/utils");
|
|
15
|
+
const matchingAlgorithm_1 = require("./utils/matchingAlgorithm");
|
|
16
16
|
const middlewares_1 = require("./utils/middlewares");
|
|
17
17
|
const queryString_1 = require("./utils/queryString");
|
|
18
18
|
function registerTagTools(server, api) {
|
|
@@ -24,7 +24,7 @@ function registerTagTools(server, api) {
|
|
|
24
24
|
name__iexact: zod_1.z.string().optional(),
|
|
25
25
|
name__istartswith: zod_1.z.string().optional(),
|
|
26
26
|
ordering: zod_1.z.string().optional(),
|
|
27
|
-
}, (0, middlewares_1.withErrorHandling)((
|
|
27
|
+
}, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
|
|
28
28
|
if (!api)
|
|
29
29
|
throw new Error("Please configure API connection first");
|
|
30
30
|
const queryString = (0, queryString_1.buildQueryString)(args);
|
|
@@ -46,13 +46,7 @@ function registerTagTools(server, api) {
|
|
|
46
46
|
.regex(/^#[0-9A-Fa-f]{6}$/)
|
|
47
47
|
.optional(),
|
|
48
48
|
match: zod_1.z.string().optional(),
|
|
49
|
-
matching_algorithm:
|
|
50
|
-
.number()
|
|
51
|
-
.int()
|
|
52
|
-
.min(0)
|
|
53
|
-
.max(6)
|
|
54
|
-
.optional()
|
|
55
|
-
.describe(types_1.MATCHING_ALGORITHM_DESCRIPTION),
|
|
49
|
+
matching_algorithm: matchingAlgorithm_1.matchingAlgorithmSchema.optional(),
|
|
56
50
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
57
51
|
if (!api)
|
|
58
52
|
throw new Error("Please configure API connection first");
|
|
@@ -75,13 +69,7 @@ function registerTagTools(server, api) {
|
|
|
75
69
|
.regex(/^#[0-9A-Fa-f]{6}$/)
|
|
76
70
|
.optional(),
|
|
77
71
|
match: zod_1.z.string().optional(),
|
|
78
|
-
matching_algorithm:
|
|
79
|
-
.number()
|
|
80
|
-
.int()
|
|
81
|
-
.min(0)
|
|
82
|
-
.max(6)
|
|
83
|
-
.optional()
|
|
84
|
-
.describe(types_1.MATCHING_ALGORITHM_DESCRIPTION),
|
|
72
|
+
matching_algorithm: matchingAlgorithm_1.matchingAlgorithmSchema.optional(),
|
|
85
73
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
86
74
|
if (!api)
|
|
87
75
|
throw new Error("Please configure API connection first");
|
|
@@ -6,8 +6,8 @@ export type CustomFieldQuery = [fieldNameOrId: string | number, operator: string
|
|
|
6
6
|
clauses: CustomFieldQuery[]
|
|
7
7
|
];
|
|
8
8
|
export declare const customFieldQuerySchema: z.ZodType<CustomFieldQuery>;
|
|
9
|
-
export declare const paperlessFilterValueSchema: z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]
|
|
10
|
-
export declare const paperlessFiltersSchema: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]
|
|
9
|
+
export declare const paperlessFilterValueSchema: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>]>;
|
|
10
|
+
export declare const paperlessFiltersSchema: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>]>>;
|
|
11
11
|
export declare const LIST_DOCUMENTS_ARGS_SHAPE: {
|
|
12
12
|
custom_field_query: z.ZodOptional<z.ZodString>;
|
|
13
13
|
page: z.ZodOptional<z.ZodNumber>;
|
|
@@ -27,8 +27,8 @@ export declare const LIST_DOCUMENTS_ARGS_SHAPE: {
|
|
|
27
27
|
export declare const QUERY_DOCUMENTS_ARGS_SHAPE: {
|
|
28
28
|
query: z.ZodOptional<z.ZodString>;
|
|
29
29
|
more_like_id: z.ZodOptional<z.ZodNumber>;
|
|
30
|
-
custom_field_query: z.ZodOptional<z.ZodType<CustomFieldQuery, z.
|
|
31
|
-
paperless_filters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]
|
|
30
|
+
custom_field_query: z.ZodOptional<z.ZodType<CustomFieldQuery, unknown, z.core.$ZodTypeInternals<CustomFieldQuery, unknown>>>;
|
|
31
|
+
paperless_filters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>]>>>;
|
|
32
32
|
page: z.ZodOptional<z.ZodNumber>;
|
|
33
33
|
page_size: z.ZodOptional<z.ZodNumber>;
|
|
34
34
|
search: z.ZodOptional<z.ZodString>;
|
|
@@ -68,7 +68,7 @@ exports.paperlessFilterValueSchema = zod_1.z.union([
|
|
|
68
68
|
paperlessFilterScalarSchema,
|
|
69
69
|
zod_1.z.array(paperlessFilterScalarSchema),
|
|
70
70
|
]);
|
|
71
|
-
exports.paperlessFiltersSchema = zod_1.z.record(exports.paperlessFilterValueSchema);
|
|
71
|
+
exports.paperlessFiltersSchema = zod_1.z.record(zod_1.z.string(), exports.paperlessFilterValueSchema);
|
|
72
72
|
const DOCUMENT_QUERY_BASE_ARGS_SHAPE = {
|
|
73
73
|
page: zod_1.z.number().optional(),
|
|
74
74
|
page_size: zod_1.z.number().optional(),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchingAlgorithmSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const types_1 = require("../../api/types");
|
|
6
|
+
// The 0-6 range the API declares is exactly the set `MatchingAlgorithm`
|
|
7
|
+
// enumerates, so narrowing to it after the check lets the PaperlessAPI request
|
|
8
|
+
// types take the parsed value without a cast at every call site.
|
|
9
|
+
exports.matchingAlgorithmSchema = zod_1.z
|
|
10
|
+
.number()
|
|
11
|
+
.int()
|
|
12
|
+
.min(0)
|
|
13
|
+
.max(6)
|
|
14
|
+
.transform((value) => value)
|
|
15
|
+
.describe(types_1.MATCHING_ALGORITHM_DESCRIPTION);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baruchiro/paperless-mcp",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server for interacting with Paperless-NGX document management system. Enables AI assistants to manage documents, tags, correspondents, and document types through the Paperless-NGX API.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
"url": "https://github.com/baruchiro/paperless-mcp/issues"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
47
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
48
48
|
"axios": "^1.9.0",
|
|
49
49
|
"express": "^5.1.0",
|
|
50
50
|
"form-data": "^4.0.2",
|
|
51
51
|
"typescript": "^5.8.3",
|
|
52
|
-
"zod": "
|
|
52
|
+
"zod": "~4.4.3"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@anthropic-ai/dxt": "^0.2.6",
|
package/paperless-mcp.dxt
CHANGED
|
Binary file
|