@baruchiro/paperless-mcp 0.0.3 → 0.1.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 +129 -1
- package/build/api/PaperlessAPI.d.ts +8 -2
- package/build/api/PaperlessAPI.js +50 -1
- package/build/api/documentEnhancer.d.ts +21 -0
- package/build/api/documentEnhancer.js +88 -0
- package/build/api/types.d.ts +47 -1
- package/build/index.js +11 -2
- package/build/tools/correspondents.js +22 -7
- package/build/tools/customFields.d.ts +3 -0
- package/build/tools/customFields.js +142 -0
- package/build/tools/documentTypes.js +22 -7
- package/build/tools/documents.js +117 -71
- package/build/tools/tags.js +15 -7
- package/build/tools/utils/empty.d.ts +2 -0
- package/build/tools/utils/empty.js +7 -0
- package/build/tools/utils/middlewares.d.ts +1 -1
- package/build/tools/utils/middlewares.js +12 -13
- package/package.json +6 -3
- package/paperless-mcp.dxt +0 -0
package/README.md
CHANGED
|
@@ -206,6 +206,15 @@ bulk_edit_documents({
|
|
|
206
206
|
add_tags: [1, 2],
|
|
207
207
|
remove_tags: [3, 4]
|
|
208
208
|
})
|
|
209
|
+
|
|
210
|
+
// Modify custom fields
|
|
211
|
+
bulk_edit_documents({
|
|
212
|
+
documents: [12, 13],
|
|
213
|
+
method: "modify_custom_fields",
|
|
214
|
+
add_custom_fields: {
|
|
215
|
+
"2": "שנה"
|
|
216
|
+
}
|
|
217
|
+
})
|
|
209
218
|
```
|
|
210
219
|
|
|
211
220
|
#### post_document
|
|
@@ -232,7 +241,8 @@ post_document({
|
|
|
232
241
|
correspondent: 1,
|
|
233
242
|
document_type: 2,
|
|
234
243
|
tags: [1, 3],
|
|
235
|
-
archive_serial_number: "2024-001"
|
|
244
|
+
archive_serial_number: "2024-001",
|
|
245
|
+
custom_fields: [1, 2]
|
|
236
246
|
})
|
|
237
247
|
```
|
|
238
248
|
|
|
@@ -313,6 +323,85 @@ create_document_type({
|
|
|
313
323
|
})
|
|
314
324
|
```
|
|
315
325
|
|
|
326
|
+
### Custom Field Operations
|
|
327
|
+
|
|
328
|
+
#### list_custom_fields
|
|
329
|
+
Get all custom fields.
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
list_custom_fields()
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### get_custom_field
|
|
336
|
+
Get a specific custom field by ID.
|
|
337
|
+
|
|
338
|
+
Parameters:
|
|
339
|
+
- id: Custom field ID
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
get_custom_field({
|
|
343
|
+
id: 1
|
|
344
|
+
})
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
#### create_custom_field
|
|
348
|
+
Create a new custom field.
|
|
349
|
+
|
|
350
|
+
Parameters:
|
|
351
|
+
- name: Custom field name
|
|
352
|
+
- data_type: One of "string", "url", "date", "boolean", "integer", "float", "monetary", "documentlink", "select"
|
|
353
|
+
- extra_data (optional): Extra data for the custom field, such as select options
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
create_custom_field({
|
|
357
|
+
name: "Invoice Number",
|
|
358
|
+
data_type: "string"
|
|
359
|
+
})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### update_custom_field
|
|
363
|
+
Update an existing custom field.
|
|
364
|
+
|
|
365
|
+
Parameters:
|
|
366
|
+
- id: Custom field ID
|
|
367
|
+
- name (optional): New custom field name
|
|
368
|
+
- data_type (optional): New data type
|
|
369
|
+
- extra_data (optional): Extra data for the custom field
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
update_custom_field({
|
|
373
|
+
id: 1,
|
|
374
|
+
name: "Updated Invoice Number",
|
|
375
|
+
data_type: "string"
|
|
376
|
+
})
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
#### delete_custom_field
|
|
380
|
+
Delete a custom field.
|
|
381
|
+
|
|
382
|
+
Parameters:
|
|
383
|
+
- id: Custom field ID
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
delete_custom_field({
|
|
387
|
+
id: 1
|
|
388
|
+
})
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### bulk_edit_custom_fields
|
|
392
|
+
Perform bulk operations on multiple custom fields.
|
|
393
|
+
|
|
394
|
+
Parameters:
|
|
395
|
+
- custom_fields: Array of custom field IDs
|
|
396
|
+
- operation: One of "delete"
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
bulk_edit_custom_fields({
|
|
400
|
+
custom_fields: [1, 2, 3],
|
|
401
|
+
operation: "delete"
|
|
402
|
+
})
|
|
403
|
+
```
|
|
404
|
+
|
|
316
405
|
## Error Handling
|
|
317
406
|
|
|
318
407
|
The server will show clear error messages if:
|
|
@@ -372,3 +461,42 @@ npm run start -- <baseUrl> <token> --http --port 3000
|
|
|
372
461
|
# Credits
|
|
373
462
|
|
|
374
463
|
This project is a fork of [nloui/paperless-mcp](https://github.com/nloui/paperless-mcp). Many thanks to the original author for their work. Contributions and improvements may be returned upstream.
|
|
464
|
+
|
|
465
|
+
## Debugging
|
|
466
|
+
|
|
467
|
+
To debug the MCP server in VS Code, use the following launch configuration:
|
|
468
|
+
|
|
469
|
+
```json
|
|
470
|
+
{
|
|
471
|
+
"type": "node",
|
|
472
|
+
"request": "launch",
|
|
473
|
+
"name": "Debug Paperless MCP (HTTP, ts-node ESM)",
|
|
474
|
+
"program": "${workspaceFolder}/node_modules/ts-node/dist/bin.js",
|
|
475
|
+
"args": [
|
|
476
|
+
"--esm",
|
|
477
|
+
"src/index.ts",
|
|
478
|
+
"--http",
|
|
479
|
+
"--baseUrl",
|
|
480
|
+
"http://your-paperless-instance:8000",
|
|
481
|
+
"--token",
|
|
482
|
+
"your-api-token",
|
|
483
|
+
"--port",
|
|
484
|
+
"3002"
|
|
485
|
+
],
|
|
486
|
+
"env": {
|
|
487
|
+
"NODE_OPTIONS": "--loader ts-node/esm",
|
|
488
|
+
},
|
|
489
|
+
"console": "integratedTerminal",
|
|
490
|
+
"skipFiles": [
|
|
491
|
+
"<node_internals>/**"
|
|
492
|
+
]
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Important:** Before debugging, uncomment the following line in `src/index.ts` (around line 175):
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000000));
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
This prevents the server from exiting immediately and allows you to set breakpoints and debug the code.
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { BulkEditDocumentsResult, Correspondent, Document, DocumentsResponse, DocumentType, GetCorrespondentsResponse, GetDocumentTypesResponse, GetTagsResponse, Tag } from "./types";
|
|
1
|
+
import { BulkEditDocumentsResult, BulkEditParameters, Correspondent, CustomField, Document, DocumentsResponse, DocumentType, GetCorrespondentsResponse, GetCustomFieldsResponse, GetDocumentTypesResponse, GetTagsResponse, Tag } from "./types";
|
|
2
2
|
export declare class PaperlessAPI {
|
|
3
3
|
private readonly baseUrl;
|
|
4
4
|
private readonly token;
|
|
5
5
|
constructor(baseUrl: string, token: string);
|
|
6
6
|
request<T = any>(path: string, options?: RequestInit): Promise<T>;
|
|
7
|
-
bulkEditDocuments(documents: number[], method: string, parameters?:
|
|
7
|
+
bulkEditDocuments(documents: number[], method: string, parameters?: BulkEditParameters): Promise<BulkEditDocumentsResult>;
|
|
8
8
|
postDocument(file: File, metadata?: Record<string, string | string[] | number | number[]>): Promise<string>;
|
|
9
9
|
getDocuments(query?: string): Promise<DocumentsResponse>;
|
|
10
10
|
getDocument(id: number): Promise<Document>;
|
|
11
|
+
updateDocument(id: number, data: Partial<Document>): Promise<Document>;
|
|
11
12
|
searchDocuments(query: string): Promise<DocumentsResponse>;
|
|
12
13
|
downloadDocument(id: number, asOriginal?: boolean): Promise<import("axios").AxiosResponse<any, any>>;
|
|
13
14
|
getTags(): Promise<GetTagsResponse>;
|
|
@@ -22,5 +23,10 @@ export declare class PaperlessAPI {
|
|
|
22
23
|
createDocumentType(data: Partial<DocumentType>): Promise<DocumentType>;
|
|
23
24
|
updateDocumentType(id: number, data: Partial<DocumentType>): Promise<DocumentType>;
|
|
24
25
|
deleteDocumentType(id: number): Promise<void>;
|
|
26
|
+
getCustomFields(): Promise<GetCustomFieldsResponse>;
|
|
27
|
+
getCustomField(id: number): Promise<CustomField>;
|
|
28
|
+
createCustomField(data: Partial<CustomField>): Promise<CustomField>;
|
|
29
|
+
updateCustomField(id: number, data: Partial<CustomField>): Promise<CustomField>;
|
|
30
|
+
deleteCustomField(id: number): Promise<void>;
|
|
25
31
|
bulkEditObjects(objects: any, objectType: any, operation: any, parameters?: {}): Promise<any>;
|
|
26
32
|
}
|
|
@@ -25,6 +25,7 @@ class PaperlessAPI {
|
|
|
25
25
|
}
|
|
26
26
|
request(path_1) {
|
|
27
27
|
return __awaiter(this, arguments, void 0, function* (path, options = {}) {
|
|
28
|
+
var _a, _b;
|
|
28
29
|
const url = `${this.baseUrl}/api${path}`;
|
|
29
30
|
const isJson = !options.body || typeof options.body === "string";
|
|
30
31
|
const mergedHeaders = Object.assign(Object.assign({ Authorization: `Token ${this.token}`, Accept: "application/json; version=5", "Accept-Language": "en-US,en;q=0.9" }, (isJson ? { "Content-Type": "application/json" } : {})), (0, utils_1.headersToObject)(options.headers));
|
|
@@ -44,7 +45,11 @@ class PaperlessAPI {
|
|
|
44
45
|
status: response.status,
|
|
45
46
|
response: body,
|
|
46
47
|
});
|
|
47
|
-
|
|
48
|
+
const errorMessage = (body === null || body === void 0 ? void 0 : body.detail) ||
|
|
49
|
+
(body === null || body === void 0 ? void 0 : body.error) ||
|
|
50
|
+
(body === null || body === void 0 ? void 0 : body.message) ||
|
|
51
|
+
`HTTP error! status: ${response.status}`;
|
|
52
|
+
throw new Error(String(errorMessage));
|
|
48
53
|
}
|
|
49
54
|
return body;
|
|
50
55
|
}
|
|
@@ -54,6 +59,8 @@ class PaperlessAPI {
|
|
|
54
59
|
message: error instanceof Error ? error.message : String(error),
|
|
55
60
|
url,
|
|
56
61
|
options,
|
|
62
|
+
responseData: (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data,
|
|
63
|
+
status: (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.status,
|
|
57
64
|
});
|
|
58
65
|
throw error;
|
|
59
66
|
}
|
|
@@ -115,6 +122,14 @@ class PaperlessAPI {
|
|
|
115
122
|
return this.request(`/documents/${id}/`);
|
|
116
123
|
});
|
|
117
124
|
}
|
|
125
|
+
updateDocument(id, data) {
|
|
126
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
127
|
+
return this.request(`/documents/${id}/`, {
|
|
128
|
+
method: "PATCH",
|
|
129
|
+
body: JSON.stringify(data),
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
118
133
|
searchDocuments(query) {
|
|
119
134
|
return __awaiter(this, void 0, void 0, function* () {
|
|
120
135
|
const response = yield this.request(`/documents/?query=${encodeURIComponent(query)}`);
|
|
@@ -220,6 +235,40 @@ class PaperlessAPI {
|
|
|
220
235
|
});
|
|
221
236
|
});
|
|
222
237
|
}
|
|
238
|
+
// Custom field operations
|
|
239
|
+
getCustomFields() {
|
|
240
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
241
|
+
return this.request("/custom_fields/");
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
getCustomField(id) {
|
|
245
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
246
|
+
return this.request(`/custom_fields/${id}/`);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
createCustomField(data) {
|
|
250
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
251
|
+
return this.request("/custom_fields/", {
|
|
252
|
+
method: "POST",
|
|
253
|
+
body: JSON.stringify(data),
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
updateCustomField(id, data) {
|
|
258
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
259
|
+
return this.request(`/custom_fields/${id}/`, {
|
|
260
|
+
method: "PUT",
|
|
261
|
+
body: JSON.stringify(data),
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
deleteCustomField(id) {
|
|
266
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
267
|
+
return this.request(`/custom_fields/${id}/`, {
|
|
268
|
+
method: "DELETE",
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
223
272
|
// Bulk object operations
|
|
224
273
|
bulkEditObjects(objects_1, objectType_1, operation_1) {
|
|
225
274
|
return __awaiter(this, arguments, void 0, function* (objects, objectType, operation, parameters = {}) {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types";
|
|
2
|
+
import { PaperlessAPI } from "./PaperlessAPI";
|
|
3
|
+
import { Document, DocumentsResponse } from "./types";
|
|
4
|
+
interface NamedItem {
|
|
5
|
+
id: number;
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
interface CustomField {
|
|
9
|
+
field: number;
|
|
10
|
+
name: string;
|
|
11
|
+
value: string | number | boolean | object | null;
|
|
12
|
+
}
|
|
13
|
+
export interface EnhancedDocument extends Omit<Document, "correspondent" | "document_type" | "tags" | "custom_fields"> {
|
|
14
|
+
correspondent: NamedItem | null;
|
|
15
|
+
document_type: NamedItem | null;
|
|
16
|
+
tags: NamedItem[];
|
|
17
|
+
custom_fields: CustomField[];
|
|
18
|
+
}
|
|
19
|
+
export declare function convertDocsWithNames(document: Document, api: PaperlessAPI): Promise<CallToolResult>;
|
|
20
|
+
export declare function convertDocsWithNames(documentsResponse: DocumentsResponse, api: PaperlessAPI): Promise<CallToolResult>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.convertDocsWithNames = convertDocsWithNames;
|
|
13
|
+
function convertDocsWithNames(input, api) {
|
|
14
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
15
|
+
if ("results" in input) {
|
|
16
|
+
const enhancedResults = yield enhanceDocumentsArray(input.results || [], api);
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: "text",
|
|
21
|
+
text: (enhancedResults === null || enhancedResults === void 0 ? void 0 : enhancedResults.length)
|
|
22
|
+
? JSON.stringify(Object.assign(Object.assign({}, input), { results: enhancedResults }))
|
|
23
|
+
: "No documents found",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (!input) {
|
|
29
|
+
return {
|
|
30
|
+
content: [
|
|
31
|
+
{
|
|
32
|
+
type: "text",
|
|
33
|
+
text: "No document found",
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const [enhanced] = yield enhanceDocumentsArray([input], api);
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{
|
|
42
|
+
type: "text",
|
|
43
|
+
text: JSON.stringify(enhanced),
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function enhanceDocumentsArray(documents, api) {
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
if (!(documents === null || documents === void 0 ? void 0 : documents.length)) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const [correspondents, documentTypes, tags, customFields] = yield Promise.all([
|
|
55
|
+
api.getCorrespondents(),
|
|
56
|
+
api.getDocumentTypes(),
|
|
57
|
+
api.getTags(),
|
|
58
|
+
api.getCustomFields(),
|
|
59
|
+
]);
|
|
60
|
+
const correspondentMap = new Map((correspondents.results || []).map((c) => [c.id, c.name]));
|
|
61
|
+
const documentTypeMap = new Map((documentTypes.results || []).map((dt) => [dt.id, dt.name]));
|
|
62
|
+
const tagMap = new Map((tags.results || []).map((tag) => [tag.id, tag.name]));
|
|
63
|
+
const customFieldMap = new Map((customFields.results || []).map((cf) => [cf.id, cf.name]));
|
|
64
|
+
return documents.map((doc) => (Object.assign(Object.assign({}, doc), { correspondent: doc.correspondent
|
|
65
|
+
? {
|
|
66
|
+
id: doc.correspondent,
|
|
67
|
+
name: correspondentMap.get(doc.correspondent) ||
|
|
68
|
+
String(doc.correspondent),
|
|
69
|
+
}
|
|
70
|
+
: null, document_type: doc.document_type
|
|
71
|
+
? {
|
|
72
|
+
id: doc.document_type,
|
|
73
|
+
name: documentTypeMap.get(doc.document_type) || String(doc.document_type),
|
|
74
|
+
}
|
|
75
|
+
: null, tags: Array.isArray(doc.tags)
|
|
76
|
+
? doc.tags.map((tagId) => ({
|
|
77
|
+
id: tagId,
|
|
78
|
+
name: tagMap.get(tagId) || String(tagId),
|
|
79
|
+
}))
|
|
80
|
+
: doc.tags, custom_fields: Array.isArray(doc.custom_fields)
|
|
81
|
+
? doc.custom_fields.map((field) => ({
|
|
82
|
+
field: field.field,
|
|
83
|
+
name: customFieldMap.get(field.field) || String(field.field),
|
|
84
|
+
value: field.value,
|
|
85
|
+
}))
|
|
86
|
+
: doc.custom_fields })));
|
|
87
|
+
});
|
|
88
|
+
}
|
package/build/api/types.d.ts
CHANGED
|
@@ -12,6 +12,21 @@ export interface Tag {
|
|
|
12
12
|
owner: number | null;
|
|
13
13
|
user_can_change: boolean;
|
|
14
14
|
}
|
|
15
|
+
export interface CustomField {
|
|
16
|
+
id: number;
|
|
17
|
+
name: string;
|
|
18
|
+
data_type: string;
|
|
19
|
+
extra_data?: Record<string, unknown> | null;
|
|
20
|
+
document_count: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CustomFieldInstance {
|
|
23
|
+
field: number;
|
|
24
|
+
value: string | number | boolean | object | null;
|
|
25
|
+
}
|
|
26
|
+
export interface CustomFieldInstanceRequest {
|
|
27
|
+
field: number;
|
|
28
|
+
value: string | number | boolean | object | null;
|
|
29
|
+
}
|
|
15
30
|
export interface PaginationResponse<T> {
|
|
16
31
|
count: number;
|
|
17
32
|
next: string | null;
|
|
@@ -21,6 +36,8 @@ export interface PaginationResponse<T> {
|
|
|
21
36
|
}
|
|
22
37
|
export interface GetTagsResponse extends PaginationResponse<Tag> {
|
|
23
38
|
}
|
|
39
|
+
export interface GetCustomFieldsResponse extends PaginationResponse<CustomField> {
|
|
40
|
+
}
|
|
24
41
|
export interface DocumentsResponse extends PaginationResponse<Document> {
|
|
25
42
|
}
|
|
26
43
|
export interface Document {
|
|
@@ -43,7 +60,7 @@ export interface Document {
|
|
|
43
60
|
user_can_change: boolean;
|
|
44
61
|
is_shared_by_requester: boolean;
|
|
45
62
|
notes: any[];
|
|
46
|
-
custom_fields:
|
|
63
|
+
custom_fields: CustomFieldInstance[];
|
|
47
64
|
page_count: number;
|
|
48
65
|
mime_type: string;
|
|
49
66
|
__search_hit__?: SearchHit;
|
|
@@ -87,3 +104,32 @@ export interface GetDocumentTypesResponse extends PaginationResponse<DocumentTyp
|
|
|
87
104
|
export interface BulkEditDocumentsResult {
|
|
88
105
|
result: string;
|
|
89
106
|
}
|
|
107
|
+
export interface BulkEditParameters {
|
|
108
|
+
assign_custom_fields?: number[];
|
|
109
|
+
assign_custom_fields_values?: CustomFieldInstanceRequest[];
|
|
110
|
+
remove_custom_fields?: number[];
|
|
111
|
+
add_tags?: number[];
|
|
112
|
+
remove_tags?: number[];
|
|
113
|
+
degrees?: number;
|
|
114
|
+
pages?: string;
|
|
115
|
+
metadata_document_id?: number;
|
|
116
|
+
delete_originals?: boolean;
|
|
117
|
+
correspondent?: number;
|
|
118
|
+
document_type?: number;
|
|
119
|
+
storage_path?: number;
|
|
120
|
+
tag?: number;
|
|
121
|
+
permissions?: {
|
|
122
|
+
owner?: number | null;
|
|
123
|
+
set_permissions?: {
|
|
124
|
+
view: {
|
|
125
|
+
users: number[];
|
|
126
|
+
groups: number[];
|
|
127
|
+
};
|
|
128
|
+
change: {
|
|
129
|
+
users: number[];
|
|
130
|
+
groups: number[];
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
merge?: boolean;
|
|
134
|
+
};
|
|
135
|
+
}
|
package/build/index.js
CHANGED
|
@@ -21,6 +21,7 @@ const express_1 = __importDefault(require("express"));
|
|
|
21
21
|
const node_util_1 = require("node:util");
|
|
22
22
|
const PaperlessAPI_1 = require("./api/PaperlessAPI");
|
|
23
23
|
const correspondents_1 = require("./tools/correspondents");
|
|
24
|
+
const customFields_1 = require("./tools/customFields");
|
|
24
25
|
const documents_1 = require("./tools/documents");
|
|
25
26
|
const documentTypes_1 = require("./tools/documentTypes");
|
|
26
27
|
const tags_1 = require("./tools/tags");
|
|
@@ -51,8 +52,15 @@ function main() {
|
|
|
51
52
|
instructions: `
|
|
52
53
|
Paperless-NGX MCP Server Instructions
|
|
53
54
|
|
|
55
|
+
⚠️ CRITICAL: Always differentiate between operations on specific documents vs operations on the entire system:
|
|
56
|
+
|
|
57
|
+
- REMOVE operations (e.g., remove_tag in bulk_edit_documents): Affect only the specified documents, items remain in the system
|
|
58
|
+
- DELETE operations (e.g., delete_tag, delete_correspondent): Permanently delete items from the entire system, affecting ALL documents that use them
|
|
59
|
+
|
|
60
|
+
When a user asks to "remove" something, prefer operations that affect specific documents. Only use DELETE operations when explicitly asked to delete from the system.
|
|
61
|
+
|
|
54
62
|
To view documents in your Paperless-NGX web interface, construct URLs using this pattern:
|
|
55
|
-
${
|
|
63
|
+
${resolvedPublicUrl}/documents/{document_id}/
|
|
56
64
|
|
|
57
65
|
Example: If your base URL is "http://localhost:8000", the web interface URL would be "http://localhost:8000/documents/123/" for document ID 123.
|
|
58
66
|
|
|
@@ -63,6 +71,7 @@ The document tools return JSON data with document IDs that you can use to constr
|
|
|
63
71
|
(0, tags_1.registerTagTools)(server, api);
|
|
64
72
|
(0, correspondents_1.registerCorrespondentTools)(server, api);
|
|
65
73
|
(0, documentTypes_1.registerDocumentTypeTools)(server, api);
|
|
74
|
+
(0, customFields_1.registerCustomFieldTools)(server, api);
|
|
66
75
|
if (useHttp) {
|
|
67
76
|
const app = (0, express_1.default)();
|
|
68
77
|
app.use(express_1.default.json());
|
|
@@ -151,11 +160,11 @@ The document tools return JSON data with document IDs that you can use to constr
|
|
|
151
160
|
app.listen(resolvedPort, () => {
|
|
152
161
|
console.log(`MCP Stateless Streamable HTTP Server listening on port ${resolvedPort}`);
|
|
153
162
|
});
|
|
163
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000000));
|
|
154
164
|
}
|
|
155
165
|
else {
|
|
156
166
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
157
167
|
yield server.connect(transport);
|
|
158
|
-
console.log("MCP server running with stdio transport");
|
|
159
168
|
}
|
|
160
169
|
});
|
|
161
170
|
}
|
|
@@ -22,7 +22,7 @@ function registerCorrespondentTools(server, api) {
|
|
|
22
22
|
name__iexact: zod_1.z.string().optional(),
|
|
23
23
|
name__istartswith: zod_1.z.string().optional(),
|
|
24
24
|
ordering: zod_1.z.string().optional(),
|
|
25
|
-
}, (0, middlewares_1.
|
|
25
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
26
26
|
if (!api)
|
|
27
27
|
throw new Error("Please configure API connection first");
|
|
28
28
|
const queryString = (0, queryString_1.buildQueryString)(args);
|
|
@@ -36,7 +36,7 @@ function registerCorrespondentTools(server, api) {
|
|
|
36
36
|
],
|
|
37
37
|
};
|
|
38
38
|
})));
|
|
39
|
-
server.tool("get_correspondent", { id: zod_1.z.number() }, (0, middlewares_1.
|
|
39
|
+
server.tool("get_correspondent", { id: zod_1.z.number() }, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
40
40
|
if (!api)
|
|
41
41
|
throw new Error("Please configure API connection first");
|
|
42
42
|
const response = yield api.request(`/correspondents/${args.id}/`);
|
|
@@ -50,7 +50,7 @@ function registerCorrespondentTools(server, api) {
|
|
|
50
50
|
matching_algorithm: zod_1.z
|
|
51
51
|
.enum(["any", "all", "exact", "regular expression", "fuzzy"])
|
|
52
52
|
.optional(),
|
|
53
|
-
}, (0, middlewares_1.
|
|
53
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
54
54
|
if (!api)
|
|
55
55
|
throw new Error("Please configure API connection first");
|
|
56
56
|
const response = yield api.createCorrespondent(args);
|
|
@@ -65,7 +65,7 @@ function registerCorrespondentTools(server, api) {
|
|
|
65
65
|
matching_algorithm: zod_1.z
|
|
66
66
|
.enum(["any", "all", "exact", "regular expression", "fuzzy"])
|
|
67
67
|
.optional(),
|
|
68
|
-
}, (0, middlewares_1.
|
|
68
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
69
69
|
if (!api)
|
|
70
70
|
throw new Error("Please configure API connection first");
|
|
71
71
|
const response = yield api.request(`/correspondents/${args.id}/`, {
|
|
@@ -76,9 +76,17 @@ function registerCorrespondentTools(server, api) {
|
|
|
76
76
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
77
77
|
};
|
|
78
78
|
})));
|
|
79
|
-
server.tool("delete_correspondent",
|
|
79
|
+
server.tool("delete_correspondent", "⚠️ DESTRUCTIVE: Permanently delete a correspondent from the entire system. This will affect ALL documents that use this correspondent.", {
|
|
80
|
+
id: zod_1.z.number(),
|
|
81
|
+
confirm: zod_1.z
|
|
82
|
+
.boolean()
|
|
83
|
+
.describe("Must be true to confirm this destructive operation"),
|
|
84
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
80
85
|
if (!api)
|
|
81
86
|
throw new Error("Please configure API connection first");
|
|
87
|
+
if (!args.confirm) {
|
|
88
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
89
|
+
}
|
|
82
90
|
yield api.request(`/correspondents/${args.id}/`, { method: "DELETE" });
|
|
83
91
|
return {
|
|
84
92
|
content: [
|
|
@@ -86,9 +94,13 @@ function registerCorrespondentTools(server, api) {
|
|
|
86
94
|
],
|
|
87
95
|
};
|
|
88
96
|
})));
|
|
89
|
-
server.tool("bulk_edit_correspondents", {
|
|
97
|
+
server.tool("bulk_edit_correspondents", "Bulk edit correspondents. ⚠️ WARNING: 'delete' operation permanently removes correspondents from the entire system.", {
|
|
90
98
|
correspondent_ids: zod_1.z.array(zod_1.z.number()),
|
|
91
99
|
operation: zod_1.z.enum(["set_permissions", "delete"]),
|
|
100
|
+
confirm: zod_1.z
|
|
101
|
+
.boolean()
|
|
102
|
+
.optional()
|
|
103
|
+
.describe("Must be true when operation is 'delete' to confirm destructive operation"),
|
|
92
104
|
owner: zod_1.z.number().optional(),
|
|
93
105
|
permissions: zod_1.z
|
|
94
106
|
.object({
|
|
@@ -103,9 +115,12 @@ function registerCorrespondentTools(server, api) {
|
|
|
103
115
|
})
|
|
104
116
|
.optional(),
|
|
105
117
|
merge: zod_1.z.boolean().optional(),
|
|
106
|
-
}, (0, middlewares_1.
|
|
118
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
107
119
|
if (!api)
|
|
108
120
|
throw new Error("Please configure API connection first");
|
|
121
|
+
if (args.operation === "delete" && !args.confirm) {
|
|
122
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
123
|
+
}
|
|
109
124
|
return api.bulkEditObjects(args.correspondent_ids, "correspondents", args.operation, args.operation === "set_permissions"
|
|
110
125
|
? {
|
|
111
126
|
owner: args.owner,
|
|
@@ -0,0 +1,142 @@
|
|
|
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 __rest = (this && this.__rest) || function (s, e) {
|
|
12
|
+
var t = {};
|
|
13
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
14
|
+
t[p] = s[p];
|
|
15
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
16
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
17
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
18
|
+
t[p[i]] = s[p[i]];
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.registerCustomFieldTools = registerCustomFieldTools;
|
|
24
|
+
const zod_1 = require("zod");
|
|
25
|
+
const middlewares_1 = require("./utils/middlewares");
|
|
26
|
+
const queryString_1 = require("./utils/queryString");
|
|
27
|
+
function registerCustomFieldTools(server, api) {
|
|
28
|
+
server.tool("list_custom_fields", "List all custom fields. IMPORTANT: When a user query may refer to a custom field, you should fetch all custom fields up front (with a large enough page_size), cache them for the session, and search locally for matches by name before making further API calls. This reduces redundant requests and handles ambiguity efficiently.", {
|
|
29
|
+
page: zod_1.z.number().optional(),
|
|
30
|
+
page_size: zod_1.z.number().optional(),
|
|
31
|
+
name__icontains: zod_1.z.string().optional(),
|
|
32
|
+
name__iendswith: zod_1.z.string().optional(),
|
|
33
|
+
name__iexact: zod_1.z.string().optional(),
|
|
34
|
+
name__istartswith: zod_1.z.string().optional(),
|
|
35
|
+
ordering: zod_1.z.string().optional(),
|
|
36
|
+
}, (0, middlewares_1.withErrorHandling)((...args_1) => __awaiter(this, [...args_1], void 0, function* (args = {}) {
|
|
37
|
+
if (!api)
|
|
38
|
+
throw new Error("Please configure API connection first");
|
|
39
|
+
const queryString = (0, queryString_1.buildQueryString)(args);
|
|
40
|
+
const response = yield api.request(`/custom_fields/${queryString ? `?${queryString}` : ""}`);
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: "text",
|
|
45
|
+
text: JSON.stringify(response),
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
})));
|
|
50
|
+
server.tool("get_custom_field", { id: zod_1.z.number() }, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
if (!api)
|
|
52
|
+
throw new Error("Please configure API connection first");
|
|
53
|
+
const response = yield api.getCustomField(args.id);
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
56
|
+
};
|
|
57
|
+
})));
|
|
58
|
+
server.tool("create_custom_field", {
|
|
59
|
+
name: zod_1.z.string(),
|
|
60
|
+
data_type: zod_1.z.enum([
|
|
61
|
+
"string",
|
|
62
|
+
"url",
|
|
63
|
+
"date",
|
|
64
|
+
"boolean",
|
|
65
|
+
"integer",
|
|
66
|
+
"float",
|
|
67
|
+
"monetary",
|
|
68
|
+
"documentlink",
|
|
69
|
+
"select",
|
|
70
|
+
]),
|
|
71
|
+
extra_data: zod_1.z.record(zod_1.z.unknown()).nullable().optional(),
|
|
72
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
if (!api)
|
|
74
|
+
throw new Error("Please configure API connection first");
|
|
75
|
+
const response = yield api.createCustomField(args);
|
|
76
|
+
return {
|
|
77
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
78
|
+
};
|
|
79
|
+
})));
|
|
80
|
+
server.tool("update_custom_field", {
|
|
81
|
+
id: zod_1.z.number(),
|
|
82
|
+
name: zod_1.z.string().optional(),
|
|
83
|
+
data_type: zod_1.z
|
|
84
|
+
.enum([
|
|
85
|
+
"string",
|
|
86
|
+
"url",
|
|
87
|
+
"date",
|
|
88
|
+
"boolean",
|
|
89
|
+
"integer",
|
|
90
|
+
"float",
|
|
91
|
+
"monetary",
|
|
92
|
+
"documentlink",
|
|
93
|
+
"select",
|
|
94
|
+
])
|
|
95
|
+
.optional(),
|
|
96
|
+
extra_data: zod_1.z.record(zod_1.z.unknown()).nullable().optional(),
|
|
97
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
if (!api)
|
|
99
|
+
throw new Error("Please configure API connection first");
|
|
100
|
+
const { id } = args, data = __rest(args, ["id"]);
|
|
101
|
+
const response = yield api.updateCustomField(id, data);
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
104
|
+
};
|
|
105
|
+
})));
|
|
106
|
+
server.tool("delete_custom_field", "⚠️ DESTRUCTIVE: Permanently delete a custom field from the entire system. This will remove the field from ALL documents that use it.", {
|
|
107
|
+
id: zod_1.z.number(),
|
|
108
|
+
confirm: zod_1.z.boolean().describe("Must be true to confirm this destructive operation"),
|
|
109
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
if (!api)
|
|
111
|
+
throw new Error("Please configure API connection first");
|
|
112
|
+
if (!args.confirm) {
|
|
113
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
114
|
+
}
|
|
115
|
+
yield api.deleteCustomField(args.id);
|
|
116
|
+
return {
|
|
117
|
+
content: [
|
|
118
|
+
{ type: "text", text: JSON.stringify({ status: "deleted" }) },
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
})));
|
|
122
|
+
server.tool("bulk_edit_custom_fields", "Bulk edit custom fields. ⚠️ WARNING: 'delete' operation permanently removes custom fields from the entire system.", {
|
|
123
|
+
custom_fields: zod_1.z.array(zod_1.z.number()),
|
|
124
|
+
operation: zod_1.z.enum(["delete"]),
|
|
125
|
+
confirm: zod_1.z.boolean().optional().describe("Must be true when operation is 'delete' to confirm destructive operation"),
|
|
126
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
127
|
+
if (!api)
|
|
128
|
+
throw new Error("Please configure API connection first");
|
|
129
|
+
if (args.operation === "delete" && !args.confirm) {
|
|
130
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
131
|
+
}
|
|
132
|
+
const response = yield api.bulkEditObjects(args.custom_fields, "custom_field", args.operation);
|
|
133
|
+
return {
|
|
134
|
+
content: [
|
|
135
|
+
{
|
|
136
|
+
type: "text",
|
|
137
|
+
text: JSON.stringify(response),
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
})));
|
|
142
|
+
}
|
|
@@ -22,7 +22,7 @@ function registerDocumentTypeTools(server, api) {
|
|
|
22
22
|
name__iexact: zod_1.z.string().optional(),
|
|
23
23
|
name__istartswith: zod_1.z.string().optional(),
|
|
24
24
|
ordering: zod_1.z.string().optional(),
|
|
25
|
-
}, (0, middlewares_1.
|
|
25
|
+
}, (0, middlewares_1.withErrorHandling)((...args_1) => __awaiter(this, [...args_1], void 0, function* (args = {}, extra) {
|
|
26
26
|
if (!api)
|
|
27
27
|
throw new Error("Please configure API connection first");
|
|
28
28
|
const queryString = (0, queryString_1.buildQueryString)(args);
|
|
@@ -36,7 +36,7 @@ function registerDocumentTypeTools(server, api) {
|
|
|
36
36
|
],
|
|
37
37
|
};
|
|
38
38
|
})));
|
|
39
|
-
server.tool("get_document_type", { id: zod_1.z.number() }, (0, middlewares_1.
|
|
39
|
+
server.tool("get_document_type", { id: zod_1.z.number() }, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
40
40
|
if (!api)
|
|
41
41
|
throw new Error("Please configure API connection first");
|
|
42
42
|
const response = yield api.request(`/document_types/${args.id}/`);
|
|
@@ -50,7 +50,7 @@ function registerDocumentTypeTools(server, api) {
|
|
|
50
50
|
matching_algorithm: zod_1.z
|
|
51
51
|
.enum(["any", "all", "exact", "regular expression", "fuzzy"])
|
|
52
52
|
.optional(),
|
|
53
|
-
}, (0, middlewares_1.
|
|
53
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
54
54
|
if (!api)
|
|
55
55
|
throw new Error("Please configure API connection first");
|
|
56
56
|
const response = yield api.createDocumentType(args);
|
|
@@ -65,7 +65,7 @@ function registerDocumentTypeTools(server, api) {
|
|
|
65
65
|
matching_algorithm: zod_1.z
|
|
66
66
|
.enum(["any", "all", "exact", "regular expression", "fuzzy"])
|
|
67
67
|
.optional(),
|
|
68
|
-
}, (0, middlewares_1.
|
|
68
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
69
69
|
if (!api)
|
|
70
70
|
throw new Error("Please configure API connection first");
|
|
71
71
|
const response = yield api.request(`/document_types/${args.id}/`, {
|
|
@@ -76,9 +76,17 @@ function registerDocumentTypeTools(server, api) {
|
|
|
76
76
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
77
77
|
};
|
|
78
78
|
})));
|
|
79
|
-
server.tool("delete_document_type",
|
|
79
|
+
server.tool("delete_document_type", "⚠️ DESTRUCTIVE: Permanently delete a document type from the entire system. This will affect ALL documents that use this type.", {
|
|
80
|
+
id: zod_1.z.number(),
|
|
81
|
+
confirm: zod_1.z
|
|
82
|
+
.boolean()
|
|
83
|
+
.describe("Must be true to confirm this destructive operation"),
|
|
84
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
80
85
|
if (!api)
|
|
81
86
|
throw new Error("Please configure API connection first");
|
|
87
|
+
if (!args.confirm) {
|
|
88
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
89
|
+
}
|
|
82
90
|
yield api.request(`/document_types/${args.id}/`, { method: "DELETE" });
|
|
83
91
|
return {
|
|
84
92
|
content: [
|
|
@@ -86,9 +94,13 @@ function registerDocumentTypeTools(server, api) {
|
|
|
86
94
|
],
|
|
87
95
|
};
|
|
88
96
|
})));
|
|
89
|
-
server.tool("bulk_edit_document_types", {
|
|
97
|
+
server.tool("bulk_edit_document_types", "Bulk edit document types. ⚠️ WARNING: 'delete' operation permanently removes document types from the entire system.", {
|
|
90
98
|
document_type_ids: zod_1.z.array(zod_1.z.number()),
|
|
91
99
|
operation: zod_1.z.enum(["set_permissions", "delete"]),
|
|
100
|
+
confirm: zod_1.z
|
|
101
|
+
.boolean()
|
|
102
|
+
.optional()
|
|
103
|
+
.describe("Must be true when operation is 'delete' to confirm destructive operation"),
|
|
92
104
|
owner: zod_1.z.number().optional(),
|
|
93
105
|
permissions: zod_1.z
|
|
94
106
|
.object({
|
|
@@ -103,9 +115,12 @@ function registerDocumentTypeTools(server, api) {
|
|
|
103
115
|
})
|
|
104
116
|
.optional(),
|
|
105
117
|
merge: zod_1.z.boolean().optional(),
|
|
106
|
-
}, (0, middlewares_1.
|
|
118
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
107
119
|
if (!api)
|
|
108
120
|
throw new Error("Please configure API connection first");
|
|
121
|
+
if (args.operation === "delete" && !args.confirm) {
|
|
122
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
123
|
+
}
|
|
109
124
|
return api.bulkEditObjects(args.document_type_ids, "document_types", args.operation, args.operation === "set_permissions"
|
|
110
125
|
? {
|
|
111
126
|
owner: args.owner,
|
package/build/tools/documents.js
CHANGED
|
@@ -22,9 +22,11 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
23
|
exports.registerDocumentTools = registerDocumentTools;
|
|
24
24
|
const zod_1 = require("zod");
|
|
25
|
+
const documentEnhancer_1 = require("../api/documentEnhancer");
|
|
26
|
+
const empty_1 = require("./utils/empty");
|
|
25
27
|
const middlewares_1 = require("./utils/middlewares");
|
|
26
28
|
function registerDocumentTools(server, api) {
|
|
27
|
-
server.tool("bulk_edit_documents", {
|
|
29
|
+
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.", {
|
|
28
30
|
documents: zod_1.z.array(zod_1.z.number()),
|
|
29
31
|
method: zod_1.z.enum([
|
|
30
32
|
"set_correspondent",
|
|
@@ -33,6 +35,7 @@ function registerDocumentTools(server, api) {
|
|
|
33
35
|
"add_tag",
|
|
34
36
|
"remove_tag",
|
|
35
37
|
"modify_tags",
|
|
38
|
+
"modify_custom_fields",
|
|
36
39
|
"delete",
|
|
37
40
|
"reprocess",
|
|
38
41
|
"set_permissions",
|
|
@@ -45,8 +48,19 @@ function registerDocumentTools(server, api) {
|
|
|
45
48
|
document_type: zod_1.z.number().optional(),
|
|
46
49
|
storage_path: zod_1.z.number().optional(),
|
|
47
50
|
tag: zod_1.z.number().optional(),
|
|
48
|
-
add_tags: zod_1.z.array(zod_1.z.number()).optional(),
|
|
49
|
-
remove_tags: zod_1.z.array(zod_1.z.number()).optional(),
|
|
51
|
+
add_tags: zod_1.z.array(zod_1.z.number()).optional().transform(empty_1.arrayNotEmpty),
|
|
52
|
+
remove_tags: zod_1.z.array(zod_1.z.number()).optional().transform(empty_1.arrayNotEmpty),
|
|
53
|
+
add_custom_fields: zod_1.z
|
|
54
|
+
.array(zod_1.z.object({
|
|
55
|
+
field: zod_1.z.number(),
|
|
56
|
+
value: zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean(), zod_1.z.null()]),
|
|
57
|
+
}))
|
|
58
|
+
.optional()
|
|
59
|
+
.transform(empty_1.arrayNotEmpty),
|
|
60
|
+
remove_custom_fields: zod_1.z
|
|
61
|
+
.array(zod_1.z.number())
|
|
62
|
+
.optional()
|
|
63
|
+
.transform(empty_1.arrayNotEmpty),
|
|
50
64
|
permissions: zod_1.z
|
|
51
65
|
.object({
|
|
52
66
|
owner: zod_1.z.number().nullable().optional(),
|
|
@@ -64,16 +78,30 @@ function registerDocumentTools(server, api) {
|
|
|
64
78
|
.optional(),
|
|
65
79
|
merge: zod_1.z.boolean().optional(),
|
|
66
80
|
})
|
|
67
|
-
.optional()
|
|
81
|
+
.optional()
|
|
82
|
+
.transform(empty_1.objectNotEmpty),
|
|
68
83
|
metadata_document_id: zod_1.z.number().optional(),
|
|
69
84
|
delete_originals: zod_1.z.boolean().optional(),
|
|
70
85
|
pages: zod_1.z.string().optional(),
|
|
71
86
|
degrees: zod_1.z.number().optional(),
|
|
72
|
-
|
|
87
|
+
confirm: zod_1.z
|
|
88
|
+
.boolean()
|
|
89
|
+
.optional()
|
|
90
|
+
.describe("Must be true when method is 'delete' to confirm destructive operation"),
|
|
91
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
73
92
|
if (!api)
|
|
74
93
|
throw new Error("Please configure API connection first");
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
if (args.method === "delete" && !args.confirm) {
|
|
95
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
96
|
+
}
|
|
97
|
+
const { documents, method, add_custom_fields } = args, parameters = __rest(args, ["documents", "method", "add_custom_fields"]);
|
|
98
|
+
// Transform add_custom_fields into the two separate API parameters
|
|
99
|
+
const apiParameters = Object.assign({}, parameters);
|
|
100
|
+
if (add_custom_fields && add_custom_fields.length > 0) {
|
|
101
|
+
apiParameters.assign_custom_fields = add_custom_fields.map((cf) => cf.field);
|
|
102
|
+
apiParameters.assign_custom_fields_values = add_custom_fields;
|
|
103
|
+
}
|
|
104
|
+
const response = yield api.bulkEditDocuments(documents, method, apiParameters);
|
|
77
105
|
return {
|
|
78
106
|
content: [
|
|
79
107
|
{
|
|
@@ -94,7 +122,7 @@ function registerDocumentTools(server, api) {
|
|
|
94
122
|
tags: zod_1.z.array(zod_1.z.number()).optional(),
|
|
95
123
|
archive_serial_number: zod_1.z.string().optional(),
|
|
96
124
|
custom_fields: zod_1.z.array(zod_1.z.number()).optional(),
|
|
97
|
-
}, (0, middlewares_1.
|
|
125
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
98
126
|
if (!api)
|
|
99
127
|
throw new Error("Please configure API connection first");
|
|
100
128
|
const binaryData = Buffer.from(args.file, "base64");
|
|
@@ -126,10 +154,10 @@ function registerDocumentTools(server, api) {
|
|
|
126
154
|
document_type: zod_1.z.number().optional(),
|
|
127
155
|
tag: zod_1.z.number().optional(),
|
|
128
156
|
storage_path: zod_1.z.number().optional(),
|
|
129
|
-
|
|
130
|
-
|
|
157
|
+
created__date__gte: zod_1.z.string().optional(),
|
|
158
|
+
created__date__lte: zod_1.z.string().optional(),
|
|
131
159
|
ordering: zod_1.z.string().optional(),
|
|
132
|
-
}, (0, middlewares_1.
|
|
160
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
133
161
|
if (!api)
|
|
134
162
|
throw new Error("Please configure API connection first");
|
|
135
163
|
const query = new URLSearchParams();
|
|
@@ -147,29 +175,31 @@ function registerDocumentTools(server, api) {
|
|
|
147
175
|
query.set("tags__id", args.tag.toString());
|
|
148
176
|
if (args.storage_path)
|
|
149
177
|
query.set("storage_path__id", args.storage_path.toString());
|
|
150
|
-
if (args.
|
|
151
|
-
query.set("
|
|
152
|
-
if (args.
|
|
153
|
-
query.set("
|
|
178
|
+
if (args.created__date__gte)
|
|
179
|
+
query.set("created__date__gte", args.created__date__gte);
|
|
180
|
+
if (args.created__date__lte)
|
|
181
|
+
query.set("created__date__lte", args.created__date__lte);
|
|
154
182
|
if (args.ordering)
|
|
155
183
|
query.set("ordering", args.ordering);
|
|
156
184
|
const docsResponse = yield api.getDocuments(query.toString() ? `?${query.toString()}` : "");
|
|
157
|
-
return convertDocsWithNames(docsResponse, api);
|
|
185
|
+
return (0, documentEnhancer_1.convertDocsWithNames)(docsResponse, api);
|
|
158
186
|
})));
|
|
159
187
|
server.tool("get_document", {
|
|
160
188
|
id: zod_1.z.number(),
|
|
161
|
-
}, (0, middlewares_1.
|
|
189
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
162
190
|
if (!api)
|
|
163
191
|
throw new Error("Please configure API connection first");
|
|
164
192
|
const doc = yield api.getDocument(args.id);
|
|
165
|
-
const [correspondents, documentTypes, tags] = yield Promise.all([
|
|
193
|
+
const [correspondents, documentTypes, tags, customFields] = yield Promise.all([
|
|
166
194
|
api.getCorrespondents(),
|
|
167
195
|
api.getDocumentTypes(),
|
|
168
196
|
api.getTags(),
|
|
197
|
+
api.getCustomFields(),
|
|
169
198
|
]);
|
|
170
199
|
const correspondentMap = new Map((correspondents.results || []).map((c) => [c.id, c.name]));
|
|
171
200
|
const documentTypeMap = new Map((documentTypes.results || []).map((dt) => [dt.id, dt.name]));
|
|
172
201
|
const tagMap = new Map((tags.results || []).map((tag) => [tag.id, tag.name]));
|
|
202
|
+
const customFieldMap = new Map((customFields.results || []).map((cf) => [cf.id, cf.name]));
|
|
173
203
|
const docWithNames = Object.assign(Object.assign({}, doc), { correspondent: doc.correspondent
|
|
174
204
|
? {
|
|
175
205
|
id: doc.correspondent,
|
|
@@ -187,7 +217,13 @@ function registerDocumentTools(server, api) {
|
|
|
187
217
|
id: tagId,
|
|
188
218
|
name: tagMap.get(tagId) || String(tagId),
|
|
189
219
|
}))
|
|
190
|
-
: doc.tags
|
|
220
|
+
: doc.tags, custom_fields: Array.isArray(doc.custom_fields)
|
|
221
|
+
? doc.custom_fields.map((field) => ({
|
|
222
|
+
field: field.field,
|
|
223
|
+
name: customFieldMap.get(field.field) || String(field.field),
|
|
224
|
+
value: field.value,
|
|
225
|
+
}))
|
|
226
|
+
: doc.custom_fields });
|
|
191
227
|
return {
|
|
192
228
|
content: [
|
|
193
229
|
{
|
|
@@ -199,16 +235,16 @@ function registerDocumentTools(server, api) {
|
|
|
199
235
|
})));
|
|
200
236
|
server.tool("search_documents", "Full text search for documents. This tool is for searching document content, title, and metadata using a full text query. For general document listing or filtering by fields, use 'list_documents' instead.", {
|
|
201
237
|
query: zod_1.z.string(),
|
|
202
|
-
}, (0, middlewares_1.
|
|
238
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
203
239
|
if (!api)
|
|
204
240
|
throw new Error("Please configure API connection first");
|
|
205
241
|
const docsResponse = yield api.searchDocuments(args.query);
|
|
206
|
-
return convertDocsWithNames(docsResponse, api);
|
|
242
|
+
return (0, documentEnhancer_1.convertDocsWithNames)(docsResponse, api);
|
|
207
243
|
})));
|
|
208
244
|
server.tool("download_document", {
|
|
209
245
|
id: zod_1.z.number(),
|
|
210
246
|
original: zod_1.z.boolean().optional(),
|
|
211
|
-
}, (0, middlewares_1.
|
|
247
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
212
248
|
var _a, _b;
|
|
213
249
|
if (!api)
|
|
214
250
|
throw new Error("Please configure API connection first");
|
|
@@ -229,53 +265,63 @@ function registerDocumentTools(server, api) {
|
|
|
229
265
|
],
|
|
230
266
|
};
|
|
231
267
|
})));
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
268
|
+
server.tool("update_document", "Update a specific document with new values. This tool allows you to modify any document field including title, correspondent, document type, storage path, tags, custom fields, and more. Only the fields you specify will be updated.", {
|
|
269
|
+
id: zod_1.z.number().describe("The ID of the document to update"),
|
|
270
|
+
title: zod_1.z
|
|
271
|
+
.string()
|
|
272
|
+
.max(128)
|
|
273
|
+
.optional()
|
|
274
|
+
.describe("The new title for the document (max 128 characters)"),
|
|
275
|
+
correspondent: zod_1.z
|
|
276
|
+
.number()
|
|
277
|
+
.nullable()
|
|
278
|
+
.optional()
|
|
279
|
+
.describe("The ID of the correspondent to assign"),
|
|
280
|
+
document_type: zod_1.z
|
|
281
|
+
.number()
|
|
282
|
+
.nullable()
|
|
283
|
+
.optional()
|
|
284
|
+
.describe("The ID of the document type to assign"),
|
|
285
|
+
storage_path: zod_1.z
|
|
286
|
+
.number()
|
|
287
|
+
.nullable()
|
|
288
|
+
.optional()
|
|
289
|
+
.describe("The ID of the storage path to assign"),
|
|
290
|
+
tags: zod_1.z
|
|
291
|
+
.array(zod_1.z.number())
|
|
292
|
+
.optional()
|
|
293
|
+
.describe("Array of tag IDs to assign to the document"),
|
|
294
|
+
content: zod_1.z
|
|
295
|
+
.string()
|
|
296
|
+
.optional()
|
|
297
|
+
.describe("The raw text content of the document (used for searching)"),
|
|
298
|
+
created: zod_1.z
|
|
299
|
+
.string()
|
|
300
|
+
.optional()
|
|
301
|
+
.describe("The creation date in YYYY-MM-DD format"),
|
|
302
|
+
archive_serial_number: zod_1.z
|
|
303
|
+
.number()
|
|
304
|
+
.optional()
|
|
305
|
+
.describe("The archive serial number (0-4294967295)"),
|
|
306
|
+
owner: zod_1.z
|
|
307
|
+
.number()
|
|
308
|
+
.nullable()
|
|
309
|
+
.optional()
|
|
310
|
+
.describe("The ID of the user who owns the document"),
|
|
311
|
+
custom_fields: zod_1.z
|
|
312
|
+
.array(zod_1.z.object({
|
|
313
|
+
field: zod_1.z.number().describe("The custom field ID"),
|
|
314
|
+
value: zod_1.z
|
|
315
|
+
.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean(), zod_1.z.null()])
|
|
316
|
+
.describe("The value for the custom field"),
|
|
317
|
+
}))
|
|
318
|
+
.optional()
|
|
319
|
+
.describe("Array of custom field values to assign"),
|
|
320
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
321
|
+
if (!api)
|
|
322
|
+
throw new Error("Please configure API connection first");
|
|
323
|
+
const { id } = args, updateData = __rest(args, ["id"]);
|
|
324
|
+
const response = yield api.updateDocument(id, updateData);
|
|
325
|
+
return (0, documentEnhancer_1.convertDocsWithNames)(response, api);
|
|
326
|
+
})));
|
|
281
327
|
}
|
package/build/tools/tags.js
CHANGED
|
@@ -22,7 +22,7 @@ function registerTagTools(server, api) {
|
|
|
22
22
|
name__iexact: zod_1.z.string().optional(),
|
|
23
23
|
name__istartswith: zod_1.z.string().optional(),
|
|
24
24
|
ordering: zod_1.z.string().optional(),
|
|
25
|
-
}, (0, middlewares_1.
|
|
25
|
+
}, (0, middlewares_1.withErrorHandling)((...args_1) => __awaiter(this, [...args_1], void 0, function* (args = {}) {
|
|
26
26
|
if (!api)
|
|
27
27
|
throw new Error("Please configure API connection first");
|
|
28
28
|
const queryString = (0, queryString_1.buildQueryString)(args);
|
|
@@ -44,7 +44,7 @@ function registerTagTools(server, api) {
|
|
|
44
44
|
.optional(),
|
|
45
45
|
match: zod_1.z.string().optional(),
|
|
46
46
|
matching_algorithm: zod_1.z.number().int().min(0).max(4).optional(),
|
|
47
|
-
}, (0, middlewares_1.
|
|
47
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
48
48
|
if (!api)
|
|
49
49
|
throw new Error("Please configure API connection first");
|
|
50
50
|
const tag = yield api.createTag(args);
|
|
@@ -66,7 +66,7 @@ function registerTagTools(server, api) {
|
|
|
66
66
|
.optional(),
|
|
67
67
|
match: zod_1.z.string().optional(),
|
|
68
68
|
matching_algorithm: zod_1.z.number().int().min(0).max(4).optional(),
|
|
69
|
-
}, (0, middlewares_1.
|
|
69
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
70
70
|
if (!api)
|
|
71
71
|
throw new Error("Please configure API connection first");
|
|
72
72
|
const tag = yield api.updateTag(args.id, args);
|
|
@@ -79,11 +79,15 @@ function registerTagTools(server, api) {
|
|
|
79
79
|
],
|
|
80
80
|
};
|
|
81
81
|
})));
|
|
82
|
-
server.tool("delete_tag", {
|
|
82
|
+
server.tool("delete_tag", "⚠️ DESTRUCTIVE: Permanently delete a tag from the entire system. This will remove the tag from ALL documents that use it. Use with extreme caution.", {
|
|
83
83
|
id: zod_1.z.number(),
|
|
84
|
-
|
|
84
|
+
confirm: zod_1.z.boolean().describe("Must be true to confirm this destructive operation"),
|
|
85
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
85
86
|
if (!api)
|
|
86
87
|
throw new Error("Please configure API connection first");
|
|
88
|
+
if (!args.confirm) {
|
|
89
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
90
|
+
}
|
|
87
91
|
yield api.deleteTag(args.id);
|
|
88
92
|
return {
|
|
89
93
|
content: [
|
|
@@ -94,9 +98,10 @@ function registerTagTools(server, api) {
|
|
|
94
98
|
],
|
|
95
99
|
};
|
|
96
100
|
})));
|
|
97
|
-
server.tool("bulk_edit_tags", {
|
|
101
|
+
server.tool("bulk_edit_tags", "Bulk edit tags. ⚠️ WARNING: 'delete' operation permanently removes tags from the entire system. Use with caution.", {
|
|
98
102
|
tag_ids: zod_1.z.array(zod_1.z.number()),
|
|
99
103
|
operation: zod_1.z.enum(["set_permissions", "delete"]),
|
|
104
|
+
confirm: zod_1.z.boolean().optional().describe("Must be true when operation is 'delete' to confirm destructive operation"),
|
|
100
105
|
owner: zod_1.z.number().optional(),
|
|
101
106
|
permissions: zod_1.z
|
|
102
107
|
.object({
|
|
@@ -111,9 +116,12 @@ function registerTagTools(server, api) {
|
|
|
111
116
|
})
|
|
112
117
|
.optional(),
|
|
113
118
|
merge: zod_1.z.boolean().optional(),
|
|
114
|
-
}, (0, middlewares_1.
|
|
119
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
115
120
|
if (!api)
|
|
116
121
|
throw new Error("Please configure API connection first");
|
|
122
|
+
if (args.operation === "delete" && !args.confirm) {
|
|
123
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
124
|
+
}
|
|
117
125
|
return api.bulkEditObjects(args.tag_ids, "tags", args.operation, args.operation === "set_permissions"
|
|
118
126
|
? {
|
|
119
127
|
owner: args.owner,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.objectNotEmpty = exports.arrayNotEmpty = void 0;
|
|
4
|
+
const arrayNotEmpty = (array) => (array === null || array === void 0 ? void 0 : array.length) ? array : undefined;
|
|
5
|
+
exports.arrayNotEmpty = arrayNotEmpty;
|
|
6
|
+
const objectNotEmpty = (object) => object && Object.keys(object).length ? object : undefined;
|
|
7
|
+
exports.objectNotEmpty = objectNotEmpty;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ToolCallback } from "@modelcontextprotocol/sdk/server/mcp";
|
|
2
2
|
import { ZodRawShape } from "zod";
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const withErrorHandling: <Args extends ZodRawShape>(cb: ToolCallback<Args>) => ToolCallback<Args>;
|
|
@@ -9,24 +9,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
13
|
-
const
|
|
12
|
+
exports.withErrorHandling = void 0;
|
|
13
|
+
const withErrorHandling = (cb) => {
|
|
14
14
|
return ((args, extra) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
+
var _a, _b;
|
|
15
16
|
try {
|
|
16
17
|
return yield cb(args, extra);
|
|
17
18
|
}
|
|
18
19
|
catch (err) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
],
|
|
28
|
-
};
|
|
20
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
21
|
+
const responseData = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.data;
|
|
22
|
+
const status = (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.status;
|
|
23
|
+
throw new Error(JSON.stringify({
|
|
24
|
+
error: errorMessage,
|
|
25
|
+
responseData,
|
|
26
|
+
status,
|
|
27
|
+
}));
|
|
29
28
|
}
|
|
30
29
|
}));
|
|
31
30
|
};
|
|
32
|
-
exports.
|
|
31
|
+
exports.withErrorHandling = withErrorHandling;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baruchiro/paperless-mcp",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.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": {
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
11
|
"start": "ts-node src/index.ts",
|
|
12
12
|
"build": "tsc",
|
|
13
|
+
"dxt-pack": "dxt pack",
|
|
13
14
|
"inspect": "npm run build && npx -y @modelcontextprotocol/inspector node build/index.js",
|
|
14
|
-
"
|
|
15
|
+
"prepack": "npm run build && npm run dxt-pack"
|
|
15
16
|
},
|
|
16
17
|
"keywords": [
|
|
17
18
|
"mcp",
|
|
@@ -32,7 +33,8 @@
|
|
|
32
33
|
"files": [
|
|
33
34
|
"build",
|
|
34
35
|
"README.md",
|
|
35
|
-
"LICENSE"
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"paperless-mcp.dxt"
|
|
36
38
|
],
|
|
37
39
|
"bugs": {
|
|
38
40
|
"url": "https://github.com/baruchiro/paperless-mcp/issues"
|
|
@@ -46,6 +48,7 @@
|
|
|
46
48
|
"zod": "^3.24.1"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
51
|
+
"@anthropic-ai/dxt": "^0.2.6",
|
|
49
52
|
"@changesets/cli": "^2.29.4",
|
|
50
53
|
"@types/express": "^5.0.2",
|
|
51
54
|
"@types/node": "^22.15.17",
|
|
Binary file
|