UNPKG

@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.

120 lines (119 loc) 5.11 kB
"use strict"; 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.registerDocumentResources = registerDocumentResources; exports.readDocumentResource = readDocumentResource; const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); function registerDocumentResources(server, api) { server.resource("paperless-document-resource", new mcp_js_1.ResourceTemplate("paperless://documents/{id}/{resource}", { list: undefined, }), (uri, variables) => __awaiter(this, void 0, void 0, function* () { return readDocumentResource(api, uri, variables); })); server.resource("paperless-document-original-download", new mcp_js_1.ResourceTemplate("paperless://documents/{id}/download{?original}", { list: undefined, }), (uri, variables) => __awaiter(this, void 0, void 0, function* () { return readDocumentDownloadResource(api, uri, parseDocumentId(readVariable(variables, "id")), isTrueQueryValue(readVariable(variables, "original"))); })); } function readDocumentResource(api, uri, variables) { return __awaiter(this, void 0, void 0, function* () { assertPaperlessDocumentsUri(uri); const id = parseDocumentId(readVariable(variables, "id")); const resource = readVariable(variables, "resource").split("?")[0]; switch (resource) { case "download": return readDocumentDownloadResource(api, uri, id, isTrueQueryValue(uri.searchParams.get("original") || undefined)); case "thumbnail": case "thumb": return readDocumentThumbnailResource(api, uri, id); default: throw new Error(`Unsupported Paperless document resource: ${resource}`); } }); } function readDocumentDownloadResource(api, uri, id, original) { return __awaiter(this, void 0, void 0, function* () { const response = yield api.downloadDocument(id, original); return { contents: [ responseToResourceContents(uri.href, response, "application/octet-stream"), ], }; }); } function readDocumentThumbnailResource(api, uri, id) { return __awaiter(this, void 0, void 0, function* () { const response = yield api.getThumbnail(id); return { contents: [responseToResourceContents(uri.href, response, "image/webp")], }; }); } function responseToResourceContents(uri, response, fallbackMimeType) { const mimeType = getHeader(response, "content-type") || fallbackMimeType; const data = Buffer.from(response.data); if (isTextMimeType(mimeType)) { return { uri, mimeType, text: data.toString("utf8"), }; } return { uri, mimeType, blob: data.toString("base64"), }; } function assertPaperlessDocumentsUri(uri) { if (uri.protocol !== "paperless:" || uri.hostname !== "documents") { throw new Error(`Unsupported Paperless resource URI: ${uri.href}`); } } function parseDocumentId(idValue) { const id = Number(idValue); if (!Number.isInteger(id) || id <= 0) { throw new Error(`Invalid Paperless document id in resource URI: ${idValue}`); } return id; } function readVariable(variables, name) { const value = variables[name]; const stringValue = Array.isArray(value) ? value[0] : value; if (!stringValue) { throw new Error(`Missing ${name} in Paperless resource URI`); } return stringValue; } function isTrueQueryValue(value) { return value === "true" || value === "1"; } function getHeader(response, headerName) { const headers = response.headers; if (typeof headers.get === "function") { const value = headers.get(headerName); if (typeof value === "string") { return value; } } const value = headers[headerName] || headers[headerName.toLowerCase()]; if (Array.isArray(value)) { return String(value[0]); } return typeof value === "string" ? value : undefined; } function isTextMimeType(mimeType) { const normalizedMimeType = mimeType.split(";")[0].trim().toLowerCase(); return (normalizedMimeType.startsWith("text/") || normalizedMimeType === "application/json" || normalizedMimeType === "application/xml" || normalizedMimeType.endsWith("+json") || normalizedMimeType.endsWith("+xml")); }