@baruchiro/paperless-mcp
Version:
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.
188 lines (187 loc) • 9.88 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerMailTools = registerMailTools;
const zod_1 = require("zod");
const middlewares_1 = require("./utils/middlewares");
const queryString_1 = require("./utils/queryString");
const MAIL_RULE_ACTION_DESCRIPTION = "Mail rule action: 1=Delete, 2=Move to specified folder, 3=Mark as read/don't process read mails, 4=Flag/don't process flagged mails, 5=Tag/don't process tagged mails";
const ASSIGN_TITLE_FROM_DESCRIPTION = "Title assignment: 1=Use subject as title, 2=Use attachment filename as title, 3=Do not assign title from rule";
const ASSIGN_CORRESPONDENT_FROM_DESCRIPTION = "Correspondent assignment: 1=Do not assign, 2=Use mail address, 3=Use sender name or address, 4=Use assign_correspondent";
const ATTACHMENT_TYPE_DESCRIPTION = "Attachment type: 1=Only process attachments, 2=Process all files including inline attachments";
const CONSUMPTION_SCOPE_DESCRIPTION = "Consumption scope: 1=Only process attachments, 2=Process full mail as .eml, 3=Process full mail and attachments separately";
const PDF_LAYOUT_DESCRIPTION = "PDF layout for full-mail consumption: 0=System default, 1=Text then HTML, 2=HTML then text, 3=HTML only, 4=Text only";
const mailRuleFields = {
name: zod_1.z.string().optional(),
account: zod_1.z.number().int().optional(),
enabled: zod_1.z.boolean().optional(),
folder: zod_1.z.string().optional(),
filter_from: zod_1.z.string().nullable().optional(),
filter_to: zod_1.z.string().nullable().optional(),
filter_subject: zod_1.z.string().nullable().optional(),
filter_body: zod_1.z.string().nullable().optional(),
filter_attachment_filename_include: zod_1.z.string().nullable().optional(),
filter_attachment_filename_exclude: zod_1.z.string().nullable().optional(),
maximum_age: zod_1.z.number().int().min(0).optional(),
action: zod_1.z
.number()
.int()
.min(1)
.max(5)
.optional()
.describe(MAIL_RULE_ACTION_DESCRIPTION),
action_parameter: zod_1.z.string().nullable().optional(),
assign_title_from: zod_1.z
.number()
.int()
.min(1)
.max(3)
.optional()
.describe(ASSIGN_TITLE_FROM_DESCRIPTION),
assign_tags: zod_1.z.array(zod_1.z.number().int().nullable()).optional(),
assign_correspondent_from: zod_1.z
.number()
.int()
.min(1)
.max(4)
.optional()
.describe(ASSIGN_CORRESPONDENT_FROM_DESCRIPTION),
assign_correspondent: zod_1.z.number().int().nullable().optional(),
assign_document_type: zod_1.z.number().int().nullable().optional(),
assign_owner_from_rule: zod_1.z.boolean().optional(),
order: zod_1.z.number().int().optional(),
attachment_type: zod_1.z
.number()
.int()
.min(1)
.max(2)
.optional()
.describe(ATTACHMENT_TYPE_DESCRIPTION),
consumption_scope: zod_1.z
.number()
.int()
.min(1)
.max(3)
.optional()
.describe(CONSUMPTION_SCOPE_DESCRIPTION),
pdf_layout: zod_1.z
.number()
.int()
.min(0)
.max(4)
.optional()
.describe(PDF_LAYOUT_DESCRIPTION),
owner: zod_1.z.number().int().nullable().optional(),
};
function registerMailTools(server, api) {
server.tool("list_mail_accounts", "List Paperless mail accounts for selecting the account ID needed by mail rules. Does not expose account passwords.", {
page: zod_1.z.number().optional(),
page_size: zod_1.z.number().optional(),
}, (0, middlewares_1.withErrorHandling)((...args_1) => __awaiter(this, [...args_1], void 0, function* (args = {}) {
if (!api)
throw new Error("Please configure API connection first");
const queryString = (0, queryString_1.buildQueryString)(args);
const response = yield api.getMailAccounts(queryString);
const sanitizedResults = (response.results || []).map((account) => (Object.assign(Object.assign({}, account), { password: undefined })));
return {
content: [
{
type: "text",
text: JSON.stringify(Object.assign(Object.assign({}, response), { results: sanitizedResults })),
},
],
};
})));
server.tool("get_mail_account", "Get one Paperless mail account by ID. Password/token fields are redacted if the server returns them.", { id: zod_1.z.number().int() }, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
if (!api)
throw new Error("Please configure API connection first");
const _a = yield api.getMailAccount(args.id), { password } = _a, account = __rest(_a, ["password"]);
return {
content: [{ type: "text", text: JSON.stringify(account) }],
};
})));
server.tool("process_mail_account", "Manually run Paperless mail processing for one account. This can consume matching mails according to enabled Paperless mail rules.", { id: zod_1.z.number().int() }, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
if (!api)
throw new Error("Please configure API connection first");
yield api.processMailAccount(args.id);
return {
content: [
{ type: "text", text: JSON.stringify({ status: "processed" }) },
],
};
})));
server.tool("list_mail_rules", "List Paperless mail rules with optional pagination.", {
page: zod_1.z.number().optional(),
page_size: zod_1.z.number().optional(),
}, (0, middlewares_1.withErrorHandling)((...args_1) => __awaiter(this, [...args_1], void 0, function* (args = {}) {
if (!api)
throw new Error("Please configure API connection first");
const queryString = (0, queryString_1.buildQueryString)(args);
const response = yield api.getMailRules(queryString);
return {
content: [{ type: "text", text: JSON.stringify(response) }],
};
})));
server.tool("get_mail_rule", "Get one Paperless mail rule by ID.", { id: zod_1.z.number().int() }, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
if (!api)
throw new Error("Please configure API connection first");
const response = yield api.getMailRule(args.id);
return {
content: [{ type: "text", text: JSON.stringify(response) }],
};
})));
server.tool("create_mail_rule", "Create a Paperless mail rule. Use list_mail_accounts first to choose account. Prefer attachment-only rules for invoices unless the full mail must be archived.", Object.assign(Object.assign({}, mailRuleFields), { name: zod_1.z.string(), account: zod_1.z.number().int(), folder: zod_1.z.string() }), (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
if (!api)
throw new Error("Please configure API connection first");
const response = yield api.createMailRule(args);
return {
content: [{ type: "text", text: JSON.stringify(response) }],
};
})));
server.tool("update_mail_rule", "Patch an existing Paperless mail rule. Only supplied fields are changed.", Object.assign({ id: zod_1.z.number().int() }, mailRuleFields), (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
if (!api)
throw new Error("Please configure API connection first");
const { id } = args, data = __rest(args, ["id"]);
const response = yield api.updateMailRule(id, data);
return {
content: [{ type: "text", text: JSON.stringify(response) }],
};
})));
server.tool("delete_mail_rule", "Delete one Paperless mail rule. This changes future mail ingestion behavior but does not delete documents.", {
id: zod_1.z.number().int(),
confirm: zod_1.z
.boolean()
.describe("Must be true to confirm deleting the rule"),
}, (0, middlewares_1.withErrorHandling)((args) => __awaiter(this, void 0, void 0, function* () {
if (!api)
throw new Error("Please configure API connection first");
if (!args.confirm) {
throw new Error("Confirmation required. Set confirm: true to delete the mail rule.");
}
yield api.deleteMailRule(args.id);
return {
content: [
{ type: "text", text: JSON.stringify({ status: "deleted" }) },
],
};
})));
}