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.

125 lines (124 loc) 5.37 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const strict_1 = __importDefault(require("node:assert/strict")); const node_test_1 = require("node:test"); const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js"); const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const paperlessApi_1 = require("../test/mocks/paperlessApi"); const documents_1 = require("./documents"); class TestTransport { start() { return __awaiter(this, void 0, void 0, function* () { }); } send(message) { return __awaiter(this, void 0, void 0, function* () { 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); }); }); } close() { return __awaiter(this, void 0, void 0, function* () { var _a; (_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this); }); } } function createTransportPair() { const clientTransport = new TestTransport(); const serverTransport = new TestTransport(); clientTransport.peer = serverTransport; serverTransport.peer = clientTransport; return { clientTransport, serverTransport }; } function emptyPaginationResponse(results = [], all = []) { return { count: results.length, next: null, previous: null, all, results, }; } function createBinaryResponse(body, contentType) { return { data: Buffer.from(body), status: 200, statusText: "OK", headers: { "content-type": contentType, }, config: {}, }; } function withResourceClient(api, run) { return __awaiter(this, void 0, void 0, function* () { const server = new mcp_js_1.McpServer({ name: "paperless-test", version: "1.0.0" }); (0, documents_1.registerDocumentResources)(server, api); const client = new index_js_1.Client({ name: "paperless-test-client", version: "1.0.0" }); const { clientTransport, serverTransport } = createTransportPair(); yield server.connect(serverTransport); yield client.connect(clientTransport); try { yield run(client); } finally { yield client.close(); yield server.close(); } }); } (0, node_test_1.test)("resources/list does not enumerate documents at startup", () => __awaiter(void 0, void 0, void 0, function* () { // Documents are dynamic DMS data and must not be pre-registered as MCP // resources: enumerating them floods `resources/list` and the LLM context // window (issue #112). The list must stay empty even when documents exist; // documents are reached on demand via tools + the `resources/read` template. const api = { getDocuments: () => __awaiter(void 0, void 0, void 0, function* () { return emptyPaginationResponse([ (0, paperlessApi_1.createDocument)({ id: 1, title: "Invoice", mime_type: "application/pdf", }), (0, paperlessApi_1.createDocument)({ id: 2, title: "Receipt", mime_type: "image/png", }), ], [1, 2, 3]); }), }; yield withResourceClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () { const result = yield client.listResources(); strict_1.default.deepEqual(result.resources, []); })); })); (0, node_test_1.test)("resources/read returns text contents for text responses", () => __awaiter(void 0, void 0, void 0, function* () { const api = { getDocuments: () => __awaiter(void 0, void 0, void 0, function* () { return emptyPaginationResponse(); }), downloadDocument: () => __awaiter(void 0, void 0, void 0, function* () { return createBinaryResponse("plain text", "text/plain; charset=utf-8"); }), }; yield withResourceClient(api, (client) => __awaiter(void 0, void 0, void 0, function* () { const result = yield client.readResource({ uri: "paperless://documents/4/download", }); strict_1.default.deepEqual(result.contents, [ { uri: "paperless://documents/4/download", mimeType: "text/plain; charset=utf-8", text: "plain text", }, ]); })); }));