@baruchiro/paperless-mcp 0.0.0-test1
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/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.cursor/rules/http-transport.mdc +13 -0
- package/.cursor/rules/types-usage.mdc +18 -0
- package/.cursor/rules/typescript-mcp-migration.mdc +12 -0
- package/.dockerignore +2 -0
- package/.github/FUNDING.yml +15 -0
- package/.github/workflows/docker-publish.yml +57 -0
- package/.github/workflows/release.yml +40 -0
- package/CODE_OF_CONDUCT.md +5 -0
- package/CONTRIBUTING.md +17 -0
- package/Dockerfile +19 -0
- package/LICENSE +7 -0
- package/Paperless-ngx-REST-API.yaml +9975 -0
- package/README.md +363 -0
- package/package.json +48 -0
- package/src/api/PaperlessAPI.ts +257 -0
- package/src/api/types.ts +97 -0
- package/src/api/utils.ts +11 -0
- package/src/index.ts +165 -0
- package/src/tools/correspondents.ts +137 -0
- package/src/tools/documentTypes.ts +137 -0
- package/src/tools/documents.ts +316 -0
- package/src/tools/tags.ts +143 -0
- package/src/tools/utils/middlewares.ts +23 -0
- package/src/tools/utils/queryString.ts +9 -0
- package/tsconfig.json +108 -0
package/src/api/types.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export interface Tag {
|
|
2
|
+
id: number;
|
|
3
|
+
slug: string;
|
|
4
|
+
name: string;
|
|
5
|
+
color: string;
|
|
6
|
+
text_color: string;
|
|
7
|
+
match: string;
|
|
8
|
+
matching_algorithm: number;
|
|
9
|
+
is_insensitive: boolean;
|
|
10
|
+
is_inbox_tag: boolean;
|
|
11
|
+
document_count: number;
|
|
12
|
+
owner: number | null;
|
|
13
|
+
user_can_change: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PaginationResponse<T> {
|
|
17
|
+
count: number;
|
|
18
|
+
next: string | null;
|
|
19
|
+
previous: string | null;
|
|
20
|
+
all: number[];
|
|
21
|
+
results: T[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface GetTagsResponse extends PaginationResponse<Tag> {}
|
|
25
|
+
|
|
26
|
+
export interface DocumentsResponse extends PaginationResponse<Document> {}
|
|
27
|
+
|
|
28
|
+
export interface Document {
|
|
29
|
+
id: number;
|
|
30
|
+
correspondent: number | null;
|
|
31
|
+
document_type: number | null;
|
|
32
|
+
storage_path: string | null;
|
|
33
|
+
title: string;
|
|
34
|
+
content: string | null;
|
|
35
|
+
tags: number[];
|
|
36
|
+
created: string;
|
|
37
|
+
created_date: string;
|
|
38
|
+
modified: string;
|
|
39
|
+
added: string;
|
|
40
|
+
deleted_at: string | null;
|
|
41
|
+
archive_serial_number: string | null;
|
|
42
|
+
original_file_name: string;
|
|
43
|
+
archived_file_name: string;
|
|
44
|
+
owner: number | null;
|
|
45
|
+
user_can_change: boolean;
|
|
46
|
+
is_shared_by_requester: boolean;
|
|
47
|
+
notes: any[];
|
|
48
|
+
custom_fields: any[];
|
|
49
|
+
page_count: number;
|
|
50
|
+
mime_type: string;
|
|
51
|
+
__search_hit__?: SearchHit;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SearchHit {
|
|
55
|
+
score: number;
|
|
56
|
+
highlights: string;
|
|
57
|
+
note_highlights: string;
|
|
58
|
+
rank: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface Correspondent {
|
|
62
|
+
id: number;
|
|
63
|
+
slug: string;
|
|
64
|
+
name: string;
|
|
65
|
+
match: string;
|
|
66
|
+
matching_algorithm: number;
|
|
67
|
+
is_insensitive: boolean;
|
|
68
|
+
document_count: number;
|
|
69
|
+
last_correspondence: string;
|
|
70
|
+
owner: number | null;
|
|
71
|
+
permissions: any;
|
|
72
|
+
user_can_change: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface GetCorrespondentsResponse
|
|
76
|
+
extends PaginationResponse<Correspondent> {}
|
|
77
|
+
|
|
78
|
+
export interface DocumentType {
|
|
79
|
+
id: number;
|
|
80
|
+
slug: string;
|
|
81
|
+
name: string;
|
|
82
|
+
match: string;
|
|
83
|
+
matching_algorithm: number;
|
|
84
|
+
is_insensitive: boolean;
|
|
85
|
+
document_count: number;
|
|
86
|
+
last_correspondence: string;
|
|
87
|
+
owner: number | null;
|
|
88
|
+
permissions: any;
|
|
89
|
+
user_can_change: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface GetDocumentTypesResponse
|
|
93
|
+
extends PaginationResponse<DocumentType> {}
|
|
94
|
+
|
|
95
|
+
export interface BulkEditDocumentsResult {
|
|
96
|
+
result: string;
|
|
97
|
+
}
|
package/src/api/utils.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const headersToObject = (headers: any): Record<string, string> => {
|
|
2
|
+
if (!headers) return {};
|
|
3
|
+
if (typeof headers.forEach === "function") {
|
|
4
|
+
const obj: Record<string, string> = {};
|
|
5
|
+
headers.forEach((value: string, key: string) => {
|
|
6
|
+
obj[key] = value;
|
|
7
|
+
});
|
|
8
|
+
return obj;
|
|
9
|
+
}
|
|
10
|
+
return headers;
|
|
11
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
|
+
import express from "express";
|
|
6
|
+
import { PaperlessAPI } from "./api/PaperlessAPI";
|
|
7
|
+
import { registerCorrespondentTools } from "./tools/correspondents";
|
|
8
|
+
import { registerDocumentTools } from "./tools/documents";
|
|
9
|
+
import { registerDocumentTypeTools } from "./tools/documentTypes";
|
|
10
|
+
import { registerTagTools } from "./tools/tags";
|
|
11
|
+
|
|
12
|
+
// Simple CLI argument parsing
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const useHttp = args.includes("--http");
|
|
15
|
+
let port = 3000;
|
|
16
|
+
const portIndex = args.indexOf("--port");
|
|
17
|
+
if (portIndex !== -1 && args[portIndex + 1]) {
|
|
18
|
+
const parsed = parseInt(args[portIndex + 1], 10);
|
|
19
|
+
if (!isNaN(parsed)) port = parsed;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function main() {
|
|
23
|
+
let baseUrl: string | undefined;
|
|
24
|
+
let token: string | undefined;
|
|
25
|
+
|
|
26
|
+
if (useHttp) {
|
|
27
|
+
baseUrl = process.env.PAPERLESS_URL;
|
|
28
|
+
token = process.env.API_KEY;
|
|
29
|
+
if (!baseUrl || !token) {
|
|
30
|
+
console.error(
|
|
31
|
+
"When using --http, PAPERLESS_URL and API_KEY environment variables must be set."
|
|
32
|
+
);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
baseUrl = args[0];
|
|
37
|
+
token = args[1];
|
|
38
|
+
if (!baseUrl || !token) {
|
|
39
|
+
console.error(
|
|
40
|
+
"Usage: paperless-mcp <baseUrl> <token> [--http] [--port <port>]"
|
|
41
|
+
);
|
|
42
|
+
console.error(
|
|
43
|
+
"Example: paperless-mcp http://localhost:8000 your-api-token --http --port 3000"
|
|
44
|
+
);
|
|
45
|
+
console.error(
|
|
46
|
+
"When using --http, PAPERLESS_URL and API_KEY environment variables must be set."
|
|
47
|
+
);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Initialize API client and server once
|
|
53
|
+
const api = new PaperlessAPI(baseUrl, token);
|
|
54
|
+
const server = new McpServer({ name: "paperless-ngx", version: "1.0.0" });
|
|
55
|
+
registerDocumentTools(server, api);
|
|
56
|
+
registerTagTools(server, api);
|
|
57
|
+
registerCorrespondentTools(server, api);
|
|
58
|
+
registerDocumentTypeTools(server, api);
|
|
59
|
+
|
|
60
|
+
if (useHttp) {
|
|
61
|
+
const app = express();
|
|
62
|
+
app.use(express.json());
|
|
63
|
+
|
|
64
|
+
// Store transports for each session
|
|
65
|
+
const sseTransports: Record<string, SSEServerTransport> = {};
|
|
66
|
+
|
|
67
|
+
app.post("/mcp", async (req, res) => {
|
|
68
|
+
try {
|
|
69
|
+
const transport = new StreamableHTTPServerTransport({
|
|
70
|
+
sessionIdGenerator: undefined,
|
|
71
|
+
});
|
|
72
|
+
res.on("close", () => {
|
|
73
|
+
transport.close();
|
|
74
|
+
});
|
|
75
|
+
await server.connect(transport);
|
|
76
|
+
await transport.handleRequest(req, res, req.body);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("Error handling MCP request:", error);
|
|
79
|
+
if (!res.headersSent) {
|
|
80
|
+
res.status(500).json({
|
|
81
|
+
jsonrpc: "2.0",
|
|
82
|
+
error: {
|
|
83
|
+
code: -32603,
|
|
84
|
+
message: "Internal server error",
|
|
85
|
+
},
|
|
86
|
+
id: null,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
app.get("/mcp", async (req, res) => {
|
|
93
|
+
res.writeHead(405).end(
|
|
94
|
+
JSON.stringify({
|
|
95
|
+
jsonrpc: "2.0",
|
|
96
|
+
error: {
|
|
97
|
+
code: -32000,
|
|
98
|
+
message: "Method not allowed.",
|
|
99
|
+
},
|
|
100
|
+
id: null,
|
|
101
|
+
})
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
app.delete("/mcp", async (req, res) => {
|
|
106
|
+
res.writeHead(405).end(
|
|
107
|
+
JSON.stringify({
|
|
108
|
+
jsonrpc: "2.0",
|
|
109
|
+
error: {
|
|
110
|
+
code: -32000,
|
|
111
|
+
message: "Method not allowed.",
|
|
112
|
+
},
|
|
113
|
+
id: null,
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
app.get("/sse", async (req, res) => {
|
|
119
|
+
console.log("SSE request received");
|
|
120
|
+
try {
|
|
121
|
+
const transport = new SSEServerTransport("/messages", res);
|
|
122
|
+
sseTransports[transport.sessionId] = transport;
|
|
123
|
+
res.on("close", () => {
|
|
124
|
+
delete sseTransports[transport.sessionId];
|
|
125
|
+
transport.close();
|
|
126
|
+
});
|
|
127
|
+
await server.connect(transport);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error("Error handling SSE request:", error);
|
|
130
|
+
if (!res.headersSent) {
|
|
131
|
+
res.status(500).json({
|
|
132
|
+
jsonrpc: "2.0",
|
|
133
|
+
error: {
|
|
134
|
+
code: -32603,
|
|
135
|
+
message: "Internal server error",
|
|
136
|
+
},
|
|
137
|
+
id: null,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
app.post("/messages", async (req, res) => {
|
|
144
|
+
const sessionId = req.query.sessionId as string;
|
|
145
|
+
const transport = sseTransports[sessionId];
|
|
146
|
+
if (transport) {
|
|
147
|
+
await transport.handlePostMessage(req, res, req.body);
|
|
148
|
+
} else {
|
|
149
|
+
res.status(400).send("No transport found for sessionId");
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
app.listen(port, () => {
|
|
154
|
+
console.log(
|
|
155
|
+
`MCP Stateless Streamable HTTP Server listening on port ${port}`
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
} else {
|
|
159
|
+
const transport = new StdioServerTransport();
|
|
160
|
+
await server.connect(transport);
|
|
161
|
+
console.log("MCP server running with stdio transport");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
main().catch((e) => console.error(e.message));
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { errorMiddleware } from "./utils/middlewares";
|
|
4
|
+
import { buildQueryString } from "./utils/queryString";
|
|
5
|
+
|
|
6
|
+
export function registerCorrespondentTools(server: McpServer, api) {
|
|
7
|
+
server.tool(
|
|
8
|
+
"list_correspondents",
|
|
9
|
+
{
|
|
10
|
+
page: z.number().optional(),
|
|
11
|
+
page_size: z.number().optional(),
|
|
12
|
+
name__icontains: z.string().optional(),
|
|
13
|
+
name__iendswith: z.string().optional(),
|
|
14
|
+
name__iexact: z.string().optional(),
|
|
15
|
+
name__istartswith: z.string().optional(),
|
|
16
|
+
ordering: z.string().optional(),
|
|
17
|
+
},
|
|
18
|
+
errorMiddleware(async (args, extra) => {
|
|
19
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
20
|
+
const queryString = buildQueryString(args);
|
|
21
|
+
const response = await api.request(
|
|
22
|
+
`/correspondents/${queryString ? `?${queryString}` : ""}`
|
|
23
|
+
);
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: JSON.stringify(response),
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
})
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
server.tool(
|
|
36
|
+
"get_correspondent",
|
|
37
|
+
{ id: z.number() },
|
|
38
|
+
errorMiddleware(async (args, extra) => {
|
|
39
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
40
|
+
const response = await api.request(`/correspondents/${args.id}/`);
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
43
|
+
};
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
server.tool(
|
|
48
|
+
"create_correspondent",
|
|
49
|
+
{
|
|
50
|
+
name: z.string(),
|
|
51
|
+
match: z.string().optional(),
|
|
52
|
+
matching_algorithm: z
|
|
53
|
+
.enum(["any", "all", "exact", "regular expression", "fuzzy"])
|
|
54
|
+
.optional(),
|
|
55
|
+
},
|
|
56
|
+
errorMiddleware(async (args, extra) => {
|
|
57
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
58
|
+
const response = await api.createCorrespondent(args);
|
|
59
|
+
return {
|
|
60
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
61
|
+
};
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
server.tool(
|
|
66
|
+
"update_correspondent",
|
|
67
|
+
{
|
|
68
|
+
id: z.number(),
|
|
69
|
+
name: z.string(),
|
|
70
|
+
match: z.string().optional(),
|
|
71
|
+
matching_algorithm: z
|
|
72
|
+
.enum(["any", "all", "exact", "regular expression", "fuzzy"])
|
|
73
|
+
.optional(),
|
|
74
|
+
},
|
|
75
|
+
errorMiddleware(async (args, extra) => {
|
|
76
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
77
|
+
const response = await api.request(`/correspondents/${args.id}/`, {
|
|
78
|
+
method: "PUT",
|
|
79
|
+
body: JSON.stringify(args),
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
83
|
+
};
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
server.tool(
|
|
88
|
+
"delete_correspondent",
|
|
89
|
+
{ id: z.number() },
|
|
90
|
+
errorMiddleware(async (args, extra) => {
|
|
91
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
92
|
+
await api.request(`/correspondents/${args.id}/`, { method: "DELETE" });
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{ type: "text", text: JSON.stringify({ status: "deleted" }) },
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
server.tool(
|
|
102
|
+
"bulk_edit_correspondents",
|
|
103
|
+
{
|
|
104
|
+
correspondent_ids: z.array(z.number()),
|
|
105
|
+
operation: z.enum(["set_permissions", "delete"]),
|
|
106
|
+
owner: z.number().optional(),
|
|
107
|
+
permissions: z
|
|
108
|
+
.object({
|
|
109
|
+
view: z.object({
|
|
110
|
+
users: z.array(z.number()).optional(),
|
|
111
|
+
groups: z.array(z.number()).optional(),
|
|
112
|
+
}),
|
|
113
|
+
change: z.object({
|
|
114
|
+
users: z.array(z.number()).optional(),
|
|
115
|
+
groups: z.array(z.number()).optional(),
|
|
116
|
+
}),
|
|
117
|
+
})
|
|
118
|
+
.optional(),
|
|
119
|
+
merge: z.boolean().optional(),
|
|
120
|
+
},
|
|
121
|
+
errorMiddleware(async (args, extra) => {
|
|
122
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
123
|
+
return api.bulkEditObjects(
|
|
124
|
+
args.correspondent_ids,
|
|
125
|
+
"correspondents",
|
|
126
|
+
args.operation,
|
|
127
|
+
args.operation === "set_permissions"
|
|
128
|
+
? {
|
|
129
|
+
owner: args.owner,
|
|
130
|
+
permissions: args.permissions,
|
|
131
|
+
merge: args.merge,
|
|
132
|
+
}
|
|
133
|
+
: {}
|
|
134
|
+
);
|
|
135
|
+
})
|
|
136
|
+
);
|
|
137
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { errorMiddleware } from "./utils/middlewares";
|
|
3
|
+
import { buildQueryString } from "./utils/queryString";
|
|
4
|
+
|
|
5
|
+
export function registerDocumentTypeTools(server, api) {
|
|
6
|
+
server.tool(
|
|
7
|
+
"list_document_types",
|
|
8
|
+
"List all document types. IMPORTANT: When a user query may refer to a document type or tag, you should fetch all document types and all tags up front (with a large enough page_size), cache them for the session, and search locally for matches by name or slug before making further API calls. This reduces redundant requests and handles ambiguity between tags and document types efficiently.",
|
|
9
|
+
{
|
|
10
|
+
page: z.number().optional(),
|
|
11
|
+
page_size: z.number().optional(),
|
|
12
|
+
name__icontains: z.string().optional(),
|
|
13
|
+
name__iendswith: z.string().optional(),
|
|
14
|
+
name__iexact: z.string().optional(),
|
|
15
|
+
name__istartswith: z.string().optional(),
|
|
16
|
+
ordering: z.string().optional(),
|
|
17
|
+
},
|
|
18
|
+
errorMiddleware(async (args: any = {}, extra) => {
|
|
19
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
20
|
+
const queryString = buildQueryString(args);
|
|
21
|
+
const response = await api.request(
|
|
22
|
+
`/document_types/${queryString ? `?${queryString}` : ""}`
|
|
23
|
+
);
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: JSON.stringify(response),
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
})
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
server.tool(
|
|
36
|
+
"get_document_type",
|
|
37
|
+
{ id: z.number() },
|
|
38
|
+
errorMiddleware(async (args, extra) => {
|
|
39
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
40
|
+
const response = await api.request(`/document_types/${args.id}/`);
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
43
|
+
};
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
server.tool(
|
|
48
|
+
"create_document_type",
|
|
49
|
+
{
|
|
50
|
+
name: z.string(),
|
|
51
|
+
match: z.string().optional(),
|
|
52
|
+
matching_algorithm: z
|
|
53
|
+
.enum(["any", "all", "exact", "regular expression", "fuzzy"])
|
|
54
|
+
.optional(),
|
|
55
|
+
},
|
|
56
|
+
errorMiddleware(async (args, extra) => {
|
|
57
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
58
|
+
const response = await api.createDocumentType(args);
|
|
59
|
+
return {
|
|
60
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
61
|
+
};
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
server.tool(
|
|
66
|
+
"update_document_type",
|
|
67
|
+
{
|
|
68
|
+
id: z.number(),
|
|
69
|
+
name: z.string(),
|
|
70
|
+
match: z.string().optional(),
|
|
71
|
+
matching_algorithm: z
|
|
72
|
+
.enum(["any", "all", "exact", "regular expression", "fuzzy"])
|
|
73
|
+
.optional(),
|
|
74
|
+
},
|
|
75
|
+
errorMiddleware(async (args, extra) => {
|
|
76
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
77
|
+
const response = await api.request(`/document_types/${args.id}/`, {
|
|
78
|
+
method: "PUT",
|
|
79
|
+
body: JSON.stringify(args),
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
83
|
+
};
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
server.tool(
|
|
88
|
+
"delete_document_type",
|
|
89
|
+
{ id: z.number() },
|
|
90
|
+
errorMiddleware(async (args, extra) => {
|
|
91
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
92
|
+
await api.request(`/document_types/${args.id}/`, { method: "DELETE" });
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{ type: "text", text: JSON.stringify({ status: "deleted" }) },
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
server.tool(
|
|
102
|
+
"bulk_edit_document_types",
|
|
103
|
+
{
|
|
104
|
+
document_type_ids: z.array(z.number()),
|
|
105
|
+
operation: z.enum(["set_permissions", "delete"]),
|
|
106
|
+
owner: z.number().optional(),
|
|
107
|
+
permissions: z
|
|
108
|
+
.object({
|
|
109
|
+
view: z.object({
|
|
110
|
+
users: z.array(z.number()).optional(),
|
|
111
|
+
groups: z.array(z.number()).optional(),
|
|
112
|
+
}),
|
|
113
|
+
change: z.object({
|
|
114
|
+
users: z.array(z.number()).optional(),
|
|
115
|
+
groups: z.array(z.number()).optional(),
|
|
116
|
+
}),
|
|
117
|
+
})
|
|
118
|
+
.optional(),
|
|
119
|
+
merge: z.boolean().optional(),
|
|
120
|
+
},
|
|
121
|
+
errorMiddleware(async (args, extra) => {
|
|
122
|
+
if (!api) throw new Error("Please configure API connection first");
|
|
123
|
+
return api.bulkEditObjects(
|
|
124
|
+
args.document_type_ids,
|
|
125
|
+
"document_types",
|
|
126
|
+
args.operation,
|
|
127
|
+
args.operation === "set_permissions"
|
|
128
|
+
? {
|
|
129
|
+
owner: args.owner,
|
|
130
|
+
permissions: args.permissions,
|
|
131
|
+
merge: args.merge,
|
|
132
|
+
}
|
|
133
|
+
: {}
|
|
134
|
+
);
|
|
135
|
+
})
|
|
136
|
+
);
|
|
137
|
+
}
|