@baruchiro/paperless-mcp 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -10
- package/build/api/PaperlessAPI.d.ts +4 -2
- package/build/api/PaperlessAPI.js +16 -5
- package/build/tools/customFields.js +1 -1
- package/build/tools/documents.js +34 -8
- package/build/tools/utils/descriptions.d.ts +1 -0
- package/build/tools/utils/descriptions.js +4 -0
- package/build/tools/utils/monetary.d.ts +5 -0
- package/build/tools/utils/monetary.js +34 -0
- package/build/tools/utils/monetary.test.d.ts +1 -0
- package/build/tools/utils/monetary.test.js +25 -0
- package/package.json +2 -2
- package/paperless-mcp.dxt +0 -0
package/README.md
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
# Paperless-NGX MCP Server
|
|
4
4
|
|
|
5
|
-
[](https://smithery.ai/server/@baruchiro/paperless-mcp)
|
|
6
5
|

|
|
7
6
|
|
|
8
7
|
An MCP (Model Context Protocol) server for interacting with a Paperless-NGX API server. This server provides tools for managing documents, tags, correspondents, and document types in your Paperless-NGX instance.
|
|
@@ -11,15 +10,7 @@ An MCP (Model Context Protocol) server for interacting with a Paperless-NGX API
|
|
|
11
10
|
|
|
12
11
|
[](https://cursor.com/install-mcp?name=paperless&config=eyJjb21tYW5kIjoibnB4IC15IEBiYXJ1Y2hpcm8vcGFwZXJsZXNzLW1jcEBsYXRlc3QiLCJlbnYiOnsiUEFQRVJMRVNTX1VSTCI6Imh0dHA6Ly95b3VyLXBhcGVybGVzcy1pbnN0YW5jZTo4MDAwIiwiUEFQRVJMRVNTX0FQSV9LRVkiOiJ5b3VyLWFwaS10b2tlbiJ9fQ%3D%3D)
|
|
13
12
|
|
|
14
|
-
###
|
|
15
|
-
|
|
16
|
-
To install Paperless NGX MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@baruchiro/paperless-mcp):
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
npx -y @smithery/cli install @baruchiro/paperless-mcp --client claude
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### Manual Installation
|
|
13
|
+
### Installation
|
|
23
14
|
|
|
24
15
|
Add these to your MCP config file:
|
|
25
16
|
|
|
@@ -137,6 +128,18 @@ download_document({
|
|
|
137
128
|
})
|
|
138
129
|
```
|
|
139
130
|
|
|
131
|
+
#### get_document_thumbnail
|
|
132
|
+
Get a document thumbnail (image preview) by ID. Returns the thumbnail as a base64-encoded WebP image resource.
|
|
133
|
+
|
|
134
|
+
Parameters:
|
|
135
|
+
- id: Document ID
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
get_document_thumbnail({
|
|
139
|
+
id: 123
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
140
143
|
#### bulk_edit_documents
|
|
141
144
|
Perform bulk operations on multiple documents.
|
|
142
145
|
|
|
@@ -480,6 +483,57 @@ npm run start -- <baseUrl> <token> --http --port 3000
|
|
|
480
483
|
- Each request is handled statelessly, following the [StreamableHTTPServerTransport](https://github.com/modelcontextprotocol/typescript-sdk) pattern.
|
|
481
484
|
- GET and DELETE requests to `/mcp` will return 405 Method Not Allowed.
|
|
482
485
|
|
|
486
|
+
<details>
|
|
487
|
+
<summary>Docker Deployment</summary>
|
|
488
|
+
|
|
489
|
+
The MCP server can be deployed using Docker and Docker Compose. The Docker image automatically runs in HTTP mode with SSE (Server-Sent Events) support on port 3000.
|
|
490
|
+
|
|
491
|
+
### Docker Compose Configuration
|
|
492
|
+
|
|
493
|
+
Create a `docker-compose.yml` file:
|
|
494
|
+
|
|
495
|
+
```yaml
|
|
496
|
+
services:
|
|
497
|
+
paperless-mcp:
|
|
498
|
+
container_name: paperless-mcp
|
|
499
|
+
image: ghcr.io/baruchiro/paperless-mcp:latest
|
|
500
|
+
environment:
|
|
501
|
+
- PAPERLESS_URL=http://your-paperless-ngx-server:8000
|
|
502
|
+
- PAPERLESS_API_KEY=your-paperless-api-key
|
|
503
|
+
- PAPERLESS_PUBLIC_URL=https://paperless-ngx.yourpublicurl.com
|
|
504
|
+
ports:
|
|
505
|
+
- "3000:3000"
|
|
506
|
+
restart: unless-stopped
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Then run:
|
|
510
|
+
```bash
|
|
511
|
+
docker-compose up -d
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Using with Continue VS Code Extension
|
|
515
|
+
|
|
516
|
+
If you're using the [Continue VS Code extension](https://continue.dev/), you can configure it to use the Dockerized MCP server via SSE.
|
|
517
|
+
|
|
518
|
+
Create or edit `.continue/mcpServers/paperless-mcp.yaml` at your workspace root:
|
|
519
|
+
|
|
520
|
+
```yaml
|
|
521
|
+
name: Paperless
|
|
522
|
+
version: 0.0.1
|
|
523
|
+
schema: v1
|
|
524
|
+
mcpServers:
|
|
525
|
+
- name: Paperless
|
|
526
|
+
type: sse
|
|
527
|
+
url: http://localhost:3000/sse
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
**Notes:**
|
|
531
|
+
- Replace `localhost` with your Docker host's IP address or hostname if running on a remote server
|
|
532
|
+
- The Docker container handles authentication via environment variables, so no credentials are needed in the Continue config
|
|
533
|
+
- The SSE endpoint is available at `/sse` on the configured port (default: 3000)
|
|
534
|
+
|
|
535
|
+
</details>
|
|
536
|
+
|
|
483
537
|
# Credits
|
|
484
538
|
|
|
485
539
|
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.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AxiosResponse } from "axios";
|
|
1
2
|
import { BulkEditDocumentsResult, BulkEditParameters, Correspondent, CustomField, Document, DocumentsResponse, DocumentType, GetCorrespondentsResponse, GetCustomFieldsResponse, GetDocumentTypesResponse, GetTagsResponse, Tag } from "./types";
|
|
2
3
|
export declare class PaperlessAPI {
|
|
3
4
|
private readonly baseUrl;
|
|
@@ -5,12 +6,13 @@ export declare class PaperlessAPI {
|
|
|
5
6
|
constructor(baseUrl: string, token: string);
|
|
6
7
|
request<T = any>(path: string, options?: RequestInit): Promise<T>;
|
|
7
8
|
bulkEditDocuments(documents: number[], method: string, parameters?: BulkEditParameters): Promise<BulkEditDocumentsResult>;
|
|
8
|
-
postDocument(
|
|
9
|
+
postDocument(document: Buffer, filename: string, metadata?: Record<string, string | string[] | number | number[]>): Promise<string>;
|
|
9
10
|
getDocuments(query?: string): Promise<DocumentsResponse>;
|
|
10
11
|
getDocument(id: number): Promise<Document>;
|
|
11
12
|
updateDocument(id: number, data: Partial<Document>): Promise<Document>;
|
|
12
13
|
searchDocuments(query: string): Promise<DocumentsResponse>;
|
|
13
|
-
downloadDocument(id: number, asOriginal?: boolean): Promise<
|
|
14
|
+
downloadDocument(id: number, asOriginal?: boolean): Promise<AxiosResponse<ArrayBuffer>>;
|
|
15
|
+
getThumbnail(id: number): Promise<AxiosResponse<ArrayBuffer>>;
|
|
14
16
|
getTags(): Promise<GetTagsResponse>;
|
|
15
17
|
createTag(data: Partial<Tag>): Promise<Tag>;
|
|
16
18
|
updateTag(id: number, data: Partial<Tag>): Promise<Tag>;
|
|
@@ -79,10 +79,10 @@ class PaperlessAPI {
|
|
|
79
79
|
});
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
|
-
postDocument(
|
|
83
|
-
return __awaiter(this, arguments, void 0, function* (
|
|
82
|
+
postDocument(document_1, filename_1) {
|
|
83
|
+
return __awaiter(this, arguments, void 0, function* (document, filename, metadata = {}) {
|
|
84
84
|
const formData = new form_data_1.default();
|
|
85
|
-
formData.append("document",
|
|
85
|
+
formData.append("document", document, { filename });
|
|
86
86
|
// Add optional metadata fields
|
|
87
87
|
if (metadata.title)
|
|
88
88
|
formData.append("title", metadata.title);
|
|
@@ -98,10 +98,10 @@ class PaperlessAPI {
|
|
|
98
98
|
metadata.tags.forEach((tag) => formData.append("tags", tag));
|
|
99
99
|
}
|
|
100
100
|
if (metadata.archive_serial_number) {
|
|
101
|
-
formData.append("archive_serial_number", metadata.archive_serial_number);
|
|
101
|
+
formData.append("archive_serial_number", String(metadata.archive_serial_number));
|
|
102
102
|
}
|
|
103
103
|
if (metadata.custom_fields) {
|
|
104
|
-
metadata.custom_fields.forEach((field) => formData.append("custom_fields", field));
|
|
104
|
+
metadata.custom_fields.forEach((field) => formData.append("custom_fields", String(field)));
|
|
105
105
|
}
|
|
106
106
|
const response = yield axios_1.default.post(`${this.baseUrl}/api/documents/post_document/`, formData, {
|
|
107
107
|
headers: Object.assign({ Authorization: `Token ${this.token}` }, formData.getHeaders()),
|
|
@@ -148,6 +148,17 @@ class PaperlessAPI {
|
|
|
148
148
|
return response;
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
|
+
getThumbnail(id) {
|
|
152
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
const response = yield axios_1.default.get(`${this.baseUrl}/api/documents/${id}/thumb/`, {
|
|
154
|
+
headers: {
|
|
155
|
+
Authorization: `Token ${this.token}`,
|
|
156
|
+
},
|
|
157
|
+
responseType: "arraybuffer",
|
|
158
|
+
});
|
|
159
|
+
return response;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
151
162
|
// Tag operations
|
|
152
163
|
getTags() {
|
|
153
164
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -55,7 +55,7 @@ function registerCustomFieldTools(server, api) {
|
|
|
55
55
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
56
56
|
};
|
|
57
57
|
})));
|
|
58
|
-
server.tool("create_custom_field", "Create a new custom field with a specified data type (string, url, date, boolean, integer, float, monetary, documentlink, or select).", {
|
|
58
|
+
server.tool("create_custom_field", "Create a new custom field with a specified data type (string, url, date, boolean, integer, float, monetary, documentlink, or select). For monetary fields, values must use currency code prefix format (e.g., USD10.00, GBP123.45) — NOT trailing symbol format (e.g., 10.00$).", {
|
|
59
59
|
name: zod_1.z.string(),
|
|
60
60
|
data_type: zod_1.z.enum([
|
|
61
61
|
"string",
|
package/build/tools/documents.js
CHANGED
|
@@ -25,6 +25,8 @@ const zod_1 = require("zod");
|
|
|
25
25
|
const documentEnhancer_1 = require("../api/documentEnhancer");
|
|
26
26
|
const empty_1 = require("./utils/empty");
|
|
27
27
|
const middlewares_1 = require("./utils/middlewares");
|
|
28
|
+
const monetary_1 = require("./utils/monetary");
|
|
29
|
+
const descriptions_1 = require("./utils/descriptions");
|
|
28
30
|
function registerDocumentTools(server, api) {
|
|
29
31
|
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.", {
|
|
30
32
|
documents: zod_1.z.array(zod_1.z.number()),
|
|
@@ -59,7 +61,7 @@ function registerDocumentTools(server, api) {
|
|
|
59
61
|
zod_1.z.boolean(),
|
|
60
62
|
zod_1.z.array(zod_1.z.number()),
|
|
61
63
|
zod_1.z.null(),
|
|
62
|
-
]),
|
|
64
|
+
]).describe(descriptions_1.CUSTOM_FIELD_VALUE_DESCRIPTION),
|
|
63
65
|
}))
|
|
64
66
|
.optional()
|
|
65
67
|
.transform(empty_1.arrayNotEmpty),
|
|
@@ -101,6 +103,7 @@ function registerDocumentTools(server, api) {
|
|
|
101
103
|
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
102
104
|
}
|
|
103
105
|
const { documents, method, add_custom_fields } = args, parameters = __rest(args, ["documents", "method", "add_custom_fields"]);
|
|
106
|
+
(0, monetary_1.validateCustomFields)(add_custom_fields);
|
|
104
107
|
// Transform add_custom_fields into the two separate API parameters
|
|
105
108
|
const apiParameters = Object.assign({}, parameters);
|
|
106
109
|
if (add_custom_fields && add_custom_fields.length > 0) {
|
|
@@ -126,16 +129,19 @@ function registerDocumentTools(server, api) {
|
|
|
126
129
|
document_type: zod_1.z.number().optional(),
|
|
127
130
|
storage_path: zod_1.z.number().optional(),
|
|
128
131
|
tags: zod_1.z.array(zod_1.z.number()).optional(),
|
|
129
|
-
archive_serial_number: zod_1.z.
|
|
132
|
+
archive_serial_number: zod_1.z.number().optional(),
|
|
130
133
|
custom_fields: zod_1.z.array(zod_1.z.number()).optional(),
|
|
131
134
|
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
132
135
|
if (!api)
|
|
133
136
|
throw new Error("Please configure API connection first");
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
// Validate base64 input
|
|
138
|
+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
139
|
+
if (!base64Regex.test(args.file)) {
|
|
140
|
+
throw new Error("Invalid base64-encoded file data. Please provide a valid base64 string.");
|
|
141
|
+
}
|
|
142
|
+
const { file, filename } = args, metadata = __rest(args, ["file", "filename"]);
|
|
143
|
+
const document = Buffer.from(file, "base64");
|
|
144
|
+
const response = yield api.postDocument(document, filename, metadata);
|
|
139
145
|
let result;
|
|
140
146
|
if (typeof response === "string" && /^\d+$/.test(response)) {
|
|
141
147
|
result = { id: Number(response) };
|
|
@@ -249,6 +255,25 @@ function registerDocumentTools(server, api) {
|
|
|
249
255
|
],
|
|
250
256
|
};
|
|
251
257
|
})));
|
|
258
|
+
server.tool("get_document_thumbnail", "Get a document thumbnail (image preview) by ID. Returns the thumbnail as a base64-encoded WebP image resource.", {
|
|
259
|
+
id: zod_1.z.number(),
|
|
260
|
+
}, (0, middlewares_1.withErrorHandling)((args, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
261
|
+
if (!api)
|
|
262
|
+
throw new Error("Please configure API connection first");
|
|
263
|
+
const response = yield api.getThumbnail(args.id);
|
|
264
|
+
return {
|
|
265
|
+
content: [
|
|
266
|
+
{
|
|
267
|
+
type: "resource",
|
|
268
|
+
resource: {
|
|
269
|
+
uri: `document-${args.id}-thumb.webp`,
|
|
270
|
+
blob: Buffer.from(response.data).toString("base64"),
|
|
271
|
+
mimeType: "image/webp",
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
};
|
|
276
|
+
})));
|
|
252
277
|
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.", {
|
|
253
278
|
id: zod_1.z.number().describe("The ID of the document to update"),
|
|
254
279
|
title: zod_1.z
|
|
@@ -303,7 +328,7 @@ function registerDocumentTools(server, api) {
|
|
|
303
328
|
zod_1.z.array(zod_1.z.number()),
|
|
304
329
|
zod_1.z.null(),
|
|
305
330
|
])
|
|
306
|
-
.describe(
|
|
331
|
+
.describe(descriptions_1.CUSTOM_FIELD_VALUE_DESCRIPTION),
|
|
307
332
|
}))
|
|
308
333
|
.optional()
|
|
309
334
|
.describe("Array of custom field values to assign"),
|
|
@@ -311,6 +336,7 @@ function registerDocumentTools(server, api) {
|
|
|
311
336
|
if (!api)
|
|
312
337
|
throw new Error("Please configure API connection first");
|
|
313
338
|
const { id } = args, updateData = __rest(args, ["id"]);
|
|
339
|
+
(0, monetary_1.validateCustomFields)(updateData.custom_fields);
|
|
314
340
|
const response = yield api.updateDocument(id, updateData);
|
|
315
341
|
return (0, documentEnhancer_1.convertDocsWithNames)(response, api);
|
|
316
342
|
})));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CUSTOM_FIELD_VALUE_DESCRIPTION = "The value for the custom field. For monetary fields, use currency code prefix format (e.g., USD10.00, GBP123.45, EUR9.99) \u2014 NOT trailing symbol format (e.g., 10.00$). For documentlink fields, use a single document ID (e.g., 123) or an array of document IDs (e.g., [123, 456]).";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CUSTOM_FIELD_VALUE_DESCRIPTION = void 0;
|
|
4
|
+
exports.CUSTOM_FIELD_VALUE_DESCRIPTION = "The value for the custom field. For monetary fields, use currency code prefix format (e.g., USD10.00, GBP123.45, EUR9.99) — NOT trailing symbol format (e.g., 10.00$). For documentlink fields, use a single document ID (e.g., 123) or an array of document IDs (e.g., [123, 456]).";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getMonetaryValidationError = getMonetaryValidationError;
|
|
4
|
+
exports.validateCustomFields = validateCustomFields;
|
|
5
|
+
const SYMBOL_TO_CODE = {
|
|
6
|
+
$: "USD",
|
|
7
|
+
"€": "EUR",
|
|
8
|
+
"£": "GBP",
|
|
9
|
+
"¥": "JPY",
|
|
10
|
+
"₹": "INR",
|
|
11
|
+
"₪": "ILS",
|
|
12
|
+
};
|
|
13
|
+
const TRAILING_SYMBOL_REGEX = new RegExp(`^(\\d+(?:\\.\\d+)?)[${Object.keys(SYMBOL_TO_CODE).join("")}]$`);
|
|
14
|
+
function getMonetaryValidationError(value) {
|
|
15
|
+
const trailingMatch = TRAILING_SYMBOL_REGEX.exec(value);
|
|
16
|
+
if (trailingMatch) {
|
|
17
|
+
const amount = trailingMatch[1];
|
|
18
|
+
const symbol = value.slice(-1);
|
|
19
|
+
const code = SYMBOL_TO_CODE[symbol] || "USD";
|
|
20
|
+
const numericAmount = parseFloat(amount);
|
|
21
|
+
const formattedAmount = isNaN(numericAmount) ? amount : numericAmount.toFixed(2);
|
|
22
|
+
return (`Invalid monetary format "${value}". ` +
|
|
23
|
+
`Paperless-NGX requires the currency code as a prefix, e.g. "${code}${formattedAmount}". ` +
|
|
24
|
+
`Use the format: {CURRENCY_CODE}{amount} (e.g., USD10.00, GBP123.45, EUR9.99).`);
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function validateCustomFields(custom_fields) {
|
|
29
|
+
custom_fields === null || custom_fields === void 0 ? void 0 : custom_fields.filter((cf) => typeof cf.value === "string").forEach((cf) => {
|
|
30
|
+
const monetaryError = getMonetaryValidationError(cf.value);
|
|
31
|
+
if (monetaryError)
|
|
32
|
+
throw new Error(monetaryError);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = require("node:test");
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const monetary_1 = require("./monetary");
|
|
9
|
+
(0, node_test_1.test)("returns null for non-monetary strings", () => {
|
|
10
|
+
strict_1.default.equal((0, monetary_1.getMonetaryValidationError)("hello"), null);
|
|
11
|
+
strict_1.default.equal((0, monetary_1.getMonetaryValidationError)("some text"), null);
|
|
12
|
+
strict_1.default.equal((0, monetary_1.getMonetaryValidationError)(""), null);
|
|
13
|
+
});
|
|
14
|
+
(0, node_test_1.test)("returns null for valid monetary prefix format", () => {
|
|
15
|
+
strict_1.default.equal((0, monetary_1.getMonetaryValidationError)("USD10.00"), null);
|
|
16
|
+
strict_1.default.equal((0, monetary_1.getMonetaryValidationError)("GBP123.45"), null);
|
|
17
|
+
strict_1.default.equal((0, monetary_1.getMonetaryValidationError)("EUR9.99"), null);
|
|
18
|
+
strict_1.default.equal((0, monetary_1.getMonetaryValidationError)("ILS50.00"), null);
|
|
19
|
+
});
|
|
20
|
+
(0, node_test_1.test)("returns error for trailing currency symbol", () => {
|
|
21
|
+
const err = (0, monetary_1.getMonetaryValidationError)("10.00$");
|
|
22
|
+
strict_1.default.ok(err);
|
|
23
|
+
strict_1.default.match(err, /USD10\.00/);
|
|
24
|
+
strict_1.default.match(err, /currency code as a prefix/);
|
|
25
|
+
});
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baruchiro/paperless-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
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": {
|
|
7
7
|
"paperless-mcp": "build/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "node --require ts-node/register --test src/**/*.test.ts",
|
|
11
11
|
"start": "ts-node src/index.ts",
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"dxt-pack": "dxt pack",
|
package/paperless-mcp.dxt
CHANGED
|
Binary file
|