@baruchiro/paperless-mcp 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +288 -11
- package/build/api/PaperlessAPI.d.ts +29 -2
- package/build/api/PaperlessAPI.js +97 -8
- package/build/api/types.d.ts +47 -0
- package/build/index.js +28 -5
- package/build/resources/documents.d.ts +1 -2
- package/build/resources/documents.js +1 -36
- package/build/resources/documents.test.js +6 -18
- package/build/server.d.ts +14 -1
- package/build/server.js +17 -3
- package/build/server.test.d.ts +1 -0
- package/build/server.test.js +54 -0
- package/build/tools/documents.d.ts +2 -0
- package/build/tools/documents.js +147 -51
- package/build/tools/documents.test.js +404 -0
- package/build/tools/mail.d.ts +3 -0
- package/build/tools/mail.js +187 -0
- package/build/tools/mail.test.d.ts +1 -0
- package/build/tools/mail.test.js +212 -0
- package/build/tools/notes.d.ts +3 -0
- package/build/tools/notes.js +55 -0
- package/build/tools/notes.test.d.ts +1 -0
- package/build/tools/notes.test.js +177 -0
- package/build/tools/utils/descriptions.d.ts +1 -1
- package/build/tools/utils/descriptions.js +1 -1
- package/build/tools/utils/documentQuery.d.ts +71 -0
- package/build/tools/utils/documentQuery.js +270 -0
- package/build/tools/utils/selectFields.d.ts +11 -0
- package/build/tools/utils/selectFields.js +91 -0
- package/build/tools/utils/selectFields.test.d.ts +1 -0
- package/build/tools/utils/selectFields.test.js +164 -0
- package/package.json +1 -1
- package/paperless-mcp.dxt +0 -0
|
@@ -0,0 +1,212 @@
|
|
|
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 mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
19
|
+
const mail_1 = require("./mail");
|
|
20
|
+
class TestTransport {
|
|
21
|
+
start() {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () { });
|
|
23
|
+
}
|
|
24
|
+
send(message) {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
queueMicrotask(() => { var _a, _b; return (_b = (_a = this.peer) === null || _a === void 0 ? void 0 : _a.onmessage) === null || _b === void 0 ? void 0 : _b.call(_a, message); });
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
close() {
|
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
var _a;
|
|
32
|
+
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function createTransportPair() {
|
|
37
|
+
const clientTransport = new TestTransport();
|
|
38
|
+
const serverTransport = new TestTransport();
|
|
39
|
+
clientTransport.peer = serverTransport;
|
|
40
|
+
serverTransport.peer = clientTransport;
|
|
41
|
+
return { clientTransport, serverTransport };
|
|
42
|
+
}
|
|
43
|
+
function parseToolText(result) {
|
|
44
|
+
var _a;
|
|
45
|
+
const item = (_a = result.content) === null || _a === void 0 ? void 0 : _a[0];
|
|
46
|
+
if (!item || item.type !== "text") {
|
|
47
|
+
throw new Error("Expected text tool response");
|
|
48
|
+
}
|
|
49
|
+
return JSON.parse(item.text);
|
|
50
|
+
}
|
|
51
|
+
function withMailClient(api, run) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
const server = new mcp_js_1.McpServer({ name: "paperless-mail-test", version: "1.0.0" });
|
|
54
|
+
(0, mail_1.registerMailTools)(server, api);
|
|
55
|
+
const client = new index_js_1.Client({
|
|
56
|
+
name: "paperless-mail-test-client",
|
|
57
|
+
version: "1.0.0",
|
|
58
|
+
});
|
|
59
|
+
const { clientTransport, serverTransport } = createTransportPair();
|
|
60
|
+
yield server.connect(serverTransport);
|
|
61
|
+
yield client.connect(clientTransport);
|
|
62
|
+
try {
|
|
63
|
+
yield run(client);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
yield client.close();
|
|
67
|
+
yield server.close();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
(0, node_test_1.test)("mail tools are registered", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
72
|
+
yield withMailClient({}, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
|
+
const result = yield client.listTools();
|
|
74
|
+
const names = result.tools.map((tool) => tool.name).sort();
|
|
75
|
+
strict_1.default.deepEqual(names, [
|
|
76
|
+
"create_mail_rule",
|
|
77
|
+
"delete_mail_rule",
|
|
78
|
+
"get_mail_account",
|
|
79
|
+
"get_mail_rule",
|
|
80
|
+
"list_mail_accounts",
|
|
81
|
+
"list_mail_rules",
|
|
82
|
+
"process_mail_account",
|
|
83
|
+
"update_mail_rule",
|
|
84
|
+
]);
|
|
85
|
+
}));
|
|
86
|
+
}));
|
|
87
|
+
(0, node_test_1.test)("list_mail_accounts redacts returned passwords", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
88
|
+
const api = {
|
|
89
|
+
getMailAccounts: (queryString) => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
+
return ({
|
|
91
|
+
count: 1,
|
|
92
|
+
next: null,
|
|
93
|
+
previous: null,
|
|
94
|
+
results: [
|
|
95
|
+
{
|
|
96
|
+
id: 1,
|
|
97
|
+
name: "Inbox",
|
|
98
|
+
username: "paperless@example.test",
|
|
99
|
+
password: "secret",
|
|
100
|
+
queryString,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
yield withMailClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
|
+
const result = (yield client.callTool({
|
|
108
|
+
name: "list_mail_accounts",
|
|
109
|
+
arguments: { page: 2, page_size: 10 },
|
|
110
|
+
}));
|
|
111
|
+
const response = parseToolText(result);
|
|
112
|
+
strict_1.default.equal(response.results[0].password, undefined);
|
|
113
|
+
strict_1.default.equal(response.results[0].queryString, "page=2&page_size=10");
|
|
114
|
+
}));
|
|
115
|
+
}));
|
|
116
|
+
(0, node_test_1.test)("get_mail_account redacts returned password", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
117
|
+
const api = {
|
|
118
|
+
getMailAccount: (id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
119
|
+
return ({
|
|
120
|
+
id,
|
|
121
|
+
name: "Inbox",
|
|
122
|
+
username: "paperless@example.test",
|
|
123
|
+
password: "secret",
|
|
124
|
+
});
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
yield withMailClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
128
|
+
const result = (yield client.callTool({
|
|
129
|
+
name: "get_mail_account",
|
|
130
|
+
arguments: { id: 7 },
|
|
131
|
+
}));
|
|
132
|
+
const account = parseToolText(result);
|
|
133
|
+
strict_1.default.deepEqual(account, {
|
|
134
|
+
id: 7,
|
|
135
|
+
name: "Inbox",
|
|
136
|
+
username: "paperless@example.test",
|
|
137
|
+
});
|
|
138
|
+
}));
|
|
139
|
+
}));
|
|
140
|
+
(0, node_test_1.test)("process_mail_account reports success after processing", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
141
|
+
const calls = [];
|
|
142
|
+
const api = {
|
|
143
|
+
processMailAccount: (id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
144
|
+
calls.push(["process", id]);
|
|
145
|
+
}),
|
|
146
|
+
};
|
|
147
|
+
yield withMailClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
148
|
+
const result = (yield client.callTool({
|
|
149
|
+
name: "process_mail_account",
|
|
150
|
+
arguments: { id: 5 },
|
|
151
|
+
}));
|
|
152
|
+
const response = parseToolText(result);
|
|
153
|
+
strict_1.default.deepEqual(response, { status: "processed" });
|
|
154
|
+
}));
|
|
155
|
+
strict_1.default.deepEqual(calls, [["process", 5]]);
|
|
156
|
+
}));
|
|
157
|
+
(0, node_test_1.test)("mail rule write tools pass through payloads", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
158
|
+
const calls = [];
|
|
159
|
+
const api = {
|
|
160
|
+
createMailRule: (data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
|
+
calls.push(["create", data]);
|
|
162
|
+
return Object.assign({ id: 3 }, data);
|
|
163
|
+
}),
|
|
164
|
+
updateMailRule: (id, data) => __awaiter(void 0, void 0, void 0, function* () {
|
|
165
|
+
calls.push(["update", id, data]);
|
|
166
|
+
return Object.assign({ id }, data);
|
|
167
|
+
}),
|
|
168
|
+
deleteMailRule: (id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
169
|
+
calls.push(["delete", id]);
|
|
170
|
+
}),
|
|
171
|
+
};
|
|
172
|
+
yield withMailClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
173
|
+
yield client.callTool({
|
|
174
|
+
name: "create_mail_rule",
|
|
175
|
+
arguments: {
|
|
176
|
+
name: "Invoices",
|
|
177
|
+
account: 1,
|
|
178
|
+
folder: "INBOX",
|
|
179
|
+
filter_from: "billing@example.test",
|
|
180
|
+
assign_tags: [3, 15],
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
yield client.callTool({
|
|
184
|
+
name: "update_mail_rule",
|
|
185
|
+
arguments: {
|
|
186
|
+
id: 3,
|
|
187
|
+
enabled: false,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
yield client.callTool({
|
|
191
|
+
name: "delete_mail_rule",
|
|
192
|
+
arguments: {
|
|
193
|
+
id: 3,
|
|
194
|
+
confirm: true,
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
}));
|
|
198
|
+
strict_1.default.deepEqual(calls, [
|
|
199
|
+
[
|
|
200
|
+
"create",
|
|
201
|
+
{
|
|
202
|
+
name: "Invoices",
|
|
203
|
+
account: 1,
|
|
204
|
+
folder: "INBOX",
|
|
205
|
+
filter_from: "billing@example.test",
|
|
206
|
+
assign_tags: [3, 15],
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
["update", 3, { enabled: false }],
|
|
210
|
+
["delete", 3],
|
|
211
|
+
]);
|
|
212
|
+
}));
|
|
@@ -0,0 +1,55 @@
|
|
|
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.registerNoteTools = registerNoteTools;
|
|
13
|
+
const zod_1 = require("zod");
|
|
14
|
+
const middlewares_1 = require("./utils/middlewares");
|
|
15
|
+
function notesResult(notes) {
|
|
16
|
+
return {
|
|
17
|
+
content: [
|
|
18
|
+
{
|
|
19
|
+
type: "text",
|
|
20
|
+
text: JSON.stringify(notes),
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function registerNoteTools(server, api) {
|
|
26
|
+
server.tool("list_document_notes", "List all notes attached to a document. Notes are free-text comments on a document and are the natural place for an audit trail (e.g. \"invoice paid on X from account Y\") or progress notes on an action item.", {
|
|
27
|
+
id: zod_1.z.number().describe("The document ID"),
|
|
28
|
+
}, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
if (!api)
|
|
30
|
+
throw new Error("Please configure API connection first");
|
|
31
|
+
return notesResult(yield api.getDocumentNotes(args.id));
|
|
32
|
+
})));
|
|
33
|
+
server.tool("create_document_note", "Add a note to a document. Use this to record an audit trail or progress note directly on the document. Returns the document's full list of notes after the note is added.", {
|
|
34
|
+
id: zod_1.z.number().describe("The document ID"),
|
|
35
|
+
note: zod_1.z.string().min(1).describe("The note text to add"),
|
|
36
|
+
}, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
if (!api)
|
|
38
|
+
throw new Error("Please configure API connection first");
|
|
39
|
+
return notesResult(yield api.createDocumentNote(args.id, args.note));
|
|
40
|
+
})));
|
|
41
|
+
server.tool("delete_document_note", "⚠️ DESTRUCTIVE: Permanently delete a single note from a document by its note ID. This operation is irreversible. Returns the document's remaining notes.", {
|
|
42
|
+
id: zod_1.z.number().describe("The document ID"),
|
|
43
|
+
note_id: zod_1.z.number().describe("The ID of the note to delete"),
|
|
44
|
+
confirm: zod_1.z
|
|
45
|
+
.boolean()
|
|
46
|
+
.describe("Must be true to confirm this destructive operation"),
|
|
47
|
+
}, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
if (!api)
|
|
49
|
+
throw new Error("Please configure API connection first");
|
|
50
|
+
if (!args.confirm) {
|
|
51
|
+
throw new Error("Confirmation required for destructive operation. Set confirm: true to proceed.");
|
|
52
|
+
}
|
|
53
|
+
return notesResult(yield api.deleteDocumentNote(args.id, args.note_id));
|
|
54
|
+
})));
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,177 @@
|
|
|
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 mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
19
|
+
const notes_1 = require("./notes");
|
|
20
|
+
class TestTransport {
|
|
21
|
+
start() {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () { });
|
|
23
|
+
}
|
|
24
|
+
send(message) {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
queueMicrotask(() => { var _a, _b; return (_b = (_a = this.peer) === null || _a === void 0 ? void 0 : _a.onmessage) === null || _b === void 0 ? void 0 : _b.call(_a, message); });
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
close() {
|
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
var _a;
|
|
32
|
+
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function createTransportPair() {
|
|
37
|
+
const clientTransport = new TestTransport();
|
|
38
|
+
const serverTransport = new TestTransport();
|
|
39
|
+
clientTransport.peer = serverTransport;
|
|
40
|
+
serverTransport.peer = clientTransport;
|
|
41
|
+
return { clientTransport, serverTransport };
|
|
42
|
+
}
|
|
43
|
+
function parseToolText(result) {
|
|
44
|
+
var _a;
|
|
45
|
+
const item = (_a = result.content) === null || _a === void 0 ? void 0 : _a[0];
|
|
46
|
+
if (!item || item.type !== "text") {
|
|
47
|
+
throw new Error("Expected text tool response");
|
|
48
|
+
}
|
|
49
|
+
return JSON.parse(item.text);
|
|
50
|
+
}
|
|
51
|
+
function createNoteApi(notes = []) {
|
|
52
|
+
const calls = {
|
|
53
|
+
getDocumentNotes: [],
|
|
54
|
+
createDocumentNote: [],
|
|
55
|
+
deleteDocumentNote: [],
|
|
56
|
+
};
|
|
57
|
+
const api = {
|
|
58
|
+
getDocumentNotes: (id) => __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
calls.getDocumentNotes.push(id);
|
|
60
|
+
return notes;
|
|
61
|
+
}),
|
|
62
|
+
createDocumentNote: (id, note) => __awaiter(this, void 0, void 0, function* () {
|
|
63
|
+
calls.createDocumentNote.push([id, note]);
|
|
64
|
+
return notes;
|
|
65
|
+
}),
|
|
66
|
+
deleteDocumentNote: (id, noteId) => __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
calls.deleteDocumentNote.push([id, noteId]);
|
|
68
|
+
return notes;
|
|
69
|
+
}),
|
|
70
|
+
};
|
|
71
|
+
return { api, calls };
|
|
72
|
+
}
|
|
73
|
+
function withNoteClient(api, run) {
|
|
74
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
const server = new mcp_js_1.McpServer({ name: "paperless-note-test", version: "1.0.0" });
|
|
76
|
+
(0, notes_1.registerNoteTools)(server, api);
|
|
77
|
+
const client = new index_js_1.Client({
|
|
78
|
+
name: "paperless-note-test-client",
|
|
79
|
+
version: "1.0.0",
|
|
80
|
+
});
|
|
81
|
+
const { clientTransport, serverTransport } = createTransportPair();
|
|
82
|
+
yield server.connect(serverTransport);
|
|
83
|
+
yield client.connect(clientTransport);
|
|
84
|
+
try {
|
|
85
|
+
yield run(client);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
yield client.close();
|
|
89
|
+
yield server.close();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
(0, node_test_1.describe)("document note tools", () => {
|
|
94
|
+
const sampleNotes = [
|
|
95
|
+
{
|
|
96
|
+
id: 5,
|
|
97
|
+
note: "Paid 2026-06-30",
|
|
98
|
+
created: "2026-06-30T10:00:00Z",
|
|
99
|
+
user: { id: 3, username: "nick" },
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
(0, node_test_1.test)("create_document_note posts the note text to the document", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
103
|
+
const { api, calls } = createNoteApi(sampleNotes);
|
|
104
|
+
yield withNoteClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
105
|
+
var _a;
|
|
106
|
+
const result = (yield client.callTool({
|
|
107
|
+
name: "create_document_note",
|
|
108
|
+
arguments: { id: 1740, note: "Antwort an Finanzamt versendet" },
|
|
109
|
+
}));
|
|
110
|
+
strict_1.default.ok(!result.isError, (_a = parseToolText(result)) === null || _a === void 0 ? void 0 : _a.error);
|
|
111
|
+
strict_1.default.deepEqual(parseToolText(result), sampleNotes);
|
|
112
|
+
}));
|
|
113
|
+
strict_1.default.deepEqual(calls.createDocumentNote, [
|
|
114
|
+
[1740, "Antwort an Finanzamt versendet"],
|
|
115
|
+
]);
|
|
116
|
+
}));
|
|
117
|
+
(0, node_test_1.test)("list_document_notes fetches notes for the document", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
118
|
+
const { api, calls } = createNoteApi(sampleNotes);
|
|
119
|
+
yield withNoteClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
120
|
+
var _a;
|
|
121
|
+
const result = (yield client.callTool({
|
|
122
|
+
name: "list_document_notes",
|
|
123
|
+
arguments: { id: 42 },
|
|
124
|
+
}));
|
|
125
|
+
strict_1.default.ok(!result.isError, (_a = parseToolText(result)) === null || _a === void 0 ? void 0 : _a.error);
|
|
126
|
+
strict_1.default.deepEqual(parseToolText(result), sampleNotes);
|
|
127
|
+
}));
|
|
128
|
+
strict_1.default.deepEqual(calls.getDocumentNotes, [42]);
|
|
129
|
+
}));
|
|
130
|
+
(0, node_test_1.test)("delete_document_note removes a note by its note ID when confirmed", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
131
|
+
const { api, calls } = createNoteApi([]);
|
|
132
|
+
yield withNoteClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
133
|
+
var _a;
|
|
134
|
+
const result = (yield client.callTool({
|
|
135
|
+
name: "delete_document_note",
|
|
136
|
+
arguments: { id: 42, note_id: 5, confirm: true },
|
|
137
|
+
}));
|
|
138
|
+
strict_1.default.ok(!result.isError, (_a = parseToolText(result)) === null || _a === void 0 ? void 0 : _a.error);
|
|
139
|
+
}));
|
|
140
|
+
strict_1.default.deepEqual(calls.deleteDocumentNote, [[42, 5]]);
|
|
141
|
+
}));
|
|
142
|
+
(0, node_test_1.test)("delete_document_note refuses to delete without confirmation", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
143
|
+
const { api, calls } = createNoteApi([]);
|
|
144
|
+
yield withNoteClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
145
|
+
var _a, _b;
|
|
146
|
+
const result = (yield client.callTool({
|
|
147
|
+
name: "delete_document_note",
|
|
148
|
+
arguments: { id: 42, note_id: 5, confirm: false },
|
|
149
|
+
}));
|
|
150
|
+
strict_1.default.ok(result.isError, "expected an error when confirm is false");
|
|
151
|
+
strict_1.default.match((_b = (_a = parseToolText(result)) === null || _a === void 0 ? void 0 : _a.error) !== null && _b !== void 0 ? _b : "", /confirm/i);
|
|
152
|
+
}));
|
|
153
|
+
strict_1.default.equal(calls.deleteDocumentNote.length, 0, "no delete should be sent without confirmation");
|
|
154
|
+
}));
|
|
155
|
+
(0, node_test_1.test)("create_document_note rejects an empty note", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
156
|
+
const { api, calls } = createNoteApi(sampleNotes);
|
|
157
|
+
yield withNoteClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
158
|
+
// Depending on the installed MCP SDK version, an input-schema (zod)
|
|
159
|
+
// violation either rejects with a protocol error (-32602) or resolves
|
|
160
|
+
// with a CallToolResult carrying isError: true. Accept both so the test
|
|
161
|
+
// stays correct across SDK versions.
|
|
162
|
+
let rejected = false;
|
|
163
|
+
let result;
|
|
164
|
+
try {
|
|
165
|
+
result = (yield client.callTool({
|
|
166
|
+
name: "create_document_note",
|
|
167
|
+
arguments: { id: 1, note: "" },
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
catch (_a) {
|
|
171
|
+
rejected = true;
|
|
172
|
+
}
|
|
173
|
+
strict_1.default.ok(rejected || (result === null || result === void 0 ? void 0 : result.isError), "expected an empty note to be rejected");
|
|
174
|
+
}));
|
|
175
|
+
strict_1.default.equal(calls.createDocumentNote.length, 0, "no note should be created when validation fails");
|
|
176
|
+
}));
|
|
177
|
+
});
|
|
@@ -1 +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]).";
|
|
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]). For select fields, pass the option's label (the display text from get_custom_field / list_custom_fields); the server translates it to the encoding Paperless expects.";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
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]).";
|
|
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]). For select fields, pass the option's label (the display text from get_custom_field / list_custom_fields); the server translates it to the encoding Paperless expects.";
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const CUSTOM_FIELD_QUERY_GROUP_OPERATORS: readonly ["AND", "OR"];
|
|
3
|
+
export type CustomFieldQueryValue = string | number | boolean | null | Array<string | number | boolean | null>;
|
|
4
|
+
export type CustomFieldQuery = [fieldNameOrId: string | number, operator: string, value: CustomFieldQueryValue] | [
|
|
5
|
+
groupOperator: (typeof CUSTOM_FIELD_QUERY_GROUP_OPERATORS)[number],
|
|
6
|
+
clauses: CustomFieldQuery[]
|
|
7
|
+
];
|
|
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]>, "many">]>;
|
|
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]>, "many">]>>;
|
|
11
|
+
export declare const LIST_DOCUMENTS_ARGS_SHAPE: {
|
|
12
|
+
custom_field_query: z.ZodOptional<z.ZodString>;
|
|
13
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
14
|
+
page_size: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
search: z.ZodOptional<z.ZodString>;
|
|
16
|
+
correspondent: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
document_type: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
tag: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
storage_path: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
created__date__gte: z.ZodOptional<z.ZodString>;
|
|
21
|
+
created__date__lte: z.ZodOptional<z.ZodString>;
|
|
22
|
+
ordering: z.ZodOptional<z.ZodString>;
|
|
23
|
+
archive_serial_number: z.ZodOptional<z.ZodNumber>;
|
|
24
|
+
archive_serial_number__isnull: z.ZodOptional<z.ZodBoolean>;
|
|
25
|
+
custom_fields__icontains: z.ZodOptional<z.ZodString>;
|
|
26
|
+
};
|
|
27
|
+
export declare const QUERY_DOCUMENTS_ARGS_SHAPE: {
|
|
28
|
+
query: z.ZodOptional<z.ZodString>;
|
|
29
|
+
more_like_id: z.ZodOptional<z.ZodNumber>;
|
|
30
|
+
custom_field_query: z.ZodOptional<z.ZodType<CustomFieldQuery, z.ZodTypeDef, CustomFieldQuery>>;
|
|
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]>, "many">]>>>;
|
|
32
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
page_size: z.ZodOptional<z.ZodNumber>;
|
|
34
|
+
search: z.ZodOptional<z.ZodString>;
|
|
35
|
+
correspondent: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
document_type: z.ZodOptional<z.ZodNumber>;
|
|
37
|
+
tag: z.ZodOptional<z.ZodNumber>;
|
|
38
|
+
storage_path: z.ZodOptional<z.ZodNumber>;
|
|
39
|
+
created__date__gte: z.ZodOptional<z.ZodString>;
|
|
40
|
+
created__date__lte: z.ZodOptional<z.ZodString>;
|
|
41
|
+
ordering: z.ZodOptional<z.ZodString>;
|
|
42
|
+
archive_serial_number: z.ZodOptional<z.ZodNumber>;
|
|
43
|
+
archive_serial_number__isnull: z.ZodOptional<z.ZodBoolean>;
|
|
44
|
+
custom_fields__icontains: z.ZodOptional<z.ZodString>;
|
|
45
|
+
};
|
|
46
|
+
export declare const SEARCH_DOCUMENTS_ARGS_SHAPE: {
|
|
47
|
+
query: z.ZodString;
|
|
48
|
+
};
|
|
49
|
+
export type PaperlessFilterValue = z.infer<typeof paperlessFilterValueSchema>;
|
|
50
|
+
export type BuildDocumentQueryArgs = {
|
|
51
|
+
page?: number;
|
|
52
|
+
page_size?: number;
|
|
53
|
+
ordering?: string;
|
|
54
|
+
query?: string;
|
|
55
|
+
search?: string;
|
|
56
|
+
more_like_id?: number;
|
|
57
|
+
correspondent?: number;
|
|
58
|
+
document_type?: number;
|
|
59
|
+
tag?: number;
|
|
60
|
+
storage_path?: number;
|
|
61
|
+
created__date__gte?: string;
|
|
62
|
+
created__date__lte?: string;
|
|
63
|
+
archive_serial_number?: number;
|
|
64
|
+
archive_serial_number__isnull?: boolean;
|
|
65
|
+
custom_field_query?: string | CustomFieldQuery;
|
|
66
|
+
custom_fields__icontains?: string;
|
|
67
|
+
paperless_filters?: Record<string, PaperlessFilterValue>;
|
|
68
|
+
};
|
|
69
|
+
export declare const DOCUMENT_QUERY_PAPERLESS_FILTER_KEYS: readonly ["added__date__gt", "added__date__gte", "added__date__lt", "added__date__lte", "added__day", "added__gt", "added__gte", "added__lt", "added__lte", "added__month", "added__year", "archive_serial_number", "archive_serial_number__gt", "archive_serial_number__gte", "archive_serial_number__isnull", "archive_serial_number__lt", "archive_serial_number__lte", "checksum__icontains", "checksum__iendswith", "checksum__iexact", "checksum__istartswith", "content__icontains", "content__iendswith", "content__iexact", "content__istartswith", "correspondent__id", "correspondent__id__in", "correspondent__id__none", "correspondent__isnull", "correspondent__name__icontains", "correspondent__name__iendswith", "correspondent__name__iexact", "correspondent__name__istartswith", "created__date__gt", "created__date__gte", "created__date__lt", "created__date__lte", "created__day", "created__gt", "created__gte", "created__lt", "created__lte", "created__month", "created__year", "custom_field_query", "custom_fields__icontains", "custom_fields__id__all", "custom_fields__id__in", "custom_fields__id__none", "document_type__id", "document_type__id__in", "document_type__id__none", "document_type__isnull", "document_type__name__icontains", "document_type__name__iendswith", "document_type__name__iexact", "document_type__name__istartswith", "fields", "full_perms", "has_custom_fields", "id", "id__in", "is_in_inbox", "is_tagged", "mime_type", "modified__date__gt", "modified__date__gte", "modified__date__lt", "modified__date__lte", "modified__day", "modified__gt", "modified__gte", "modified__lt", "modified__lte", "modified__month", "modified__year", "ordering", "original_filename__icontains", "original_filename__iendswith", "original_filename__iexact", "original_filename__istartswith", "owner__id", "owner__id__in", "owner__id__none", "owner__isnull", "page", "page_size", "query", "search", "shared_by__id", "storage_path__id", "storage_path__id__in", "storage_path__id__none", "storage_path__isnull", "storage_path__name__icontains", "storage_path__name__iendswith", "storage_path__name__iexact", "storage_path__name__istartswith", "tags__id", "tags__id__all", "tags__id__in", "tags__id__none", "tags__name__icontains", "tags__name__iendswith", "tags__name__iexact", "tags__name__istartswith", "title__icontains", "title__iendswith", "title__iexact", "title__istartswith", "title_content"];
|
|
70
|
+
export declare function buildDocumentQueryString(args: BuildDocumentQueryArgs): string;
|
|
71
|
+
export {};
|