triliumnext-mcp 0.3.8-beta.4 → 0.3.8-beta.5
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/build/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { generateTools } from "./modules/toolDefinitions.js";
|
|
|
8
8
|
import { handleCreateNoteRequest, handleUpdateNoteRequest, handleAppendNoteRequest, handleDeleteNoteRequest, handleGetNoteRequest } from "./modules/noteHandler.js";
|
|
9
9
|
import { handleSearchNotesRequest } from "./modules/searchHandler.js";
|
|
10
10
|
import { handleResolveNoteRequest } from "./modules/resolveHandler.js";
|
|
11
|
-
import { handleManageAttributes } from "./modules/attributeHandler.js";
|
|
11
|
+
import { handleManageAttributes, handleReadAttributes } from "./modules/attributeHandler.js";
|
|
12
12
|
const TRILIUM_API_URL = process.env.TRILIUM_API_URL;
|
|
13
13
|
const TRILIUM_API_TOKEN = process.env.TRILIUM_API_TOKEN;
|
|
14
14
|
const PERMISSIONS = process.env.PERMISSIONS || "READ;WRITE";
|
|
@@ -73,6 +73,8 @@ class TriliumServer {
|
|
|
73
73
|
return await handleSearchNotesRequest(request.params.arguments, this.axiosInstance, this);
|
|
74
74
|
case "resolve_note_id":
|
|
75
75
|
return await handleResolveNoteRequest(request.params.arguments, this, this.axiosInstance);
|
|
76
|
+
case "read_attributes":
|
|
77
|
+
return await handleReadAttributes(request.params.arguments, this.axiosInstance, this);
|
|
76
78
|
case "manage_attributes":
|
|
77
79
|
return await handleManageAttributes(request.params.arguments, this.axiosInstance, this);
|
|
78
80
|
default:
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Processes MCP requests for attribute management operations
|
|
4
4
|
*/
|
|
5
5
|
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
-
import { manage_attributes,
|
|
6
|
+
import { manage_attributes, read_attributes } from './attributeManager.js';
|
|
7
7
|
/**
|
|
8
8
|
* Handle manage_attributes MCP request
|
|
9
9
|
*/
|
|
@@ -32,35 +32,23 @@ export async function handleManageAttributes(args, axiosInstance, permissionChec
|
|
|
32
32
|
isError: true
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
// Check
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (readOperations.includes(args.operation)) {
|
|
39
|
-
if (!permissionChecker.hasPermission("READ")) {
|
|
40
|
-
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to read attributes.");
|
|
41
|
-
}
|
|
35
|
+
// Check WRITE permission for all manage_attributes operations
|
|
36
|
+
if (!permissionChecker.hasPermission("WRITE")) {
|
|
37
|
+
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to manage attributes.");
|
|
42
38
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
39
|
+
// Validate operation
|
|
40
|
+
const validOperations = ["create", "update", "delete", "batch_create"];
|
|
41
|
+
if (!validOperations.includes(args.operation)) {
|
|
49
42
|
return {
|
|
50
43
|
content: [
|
|
51
44
|
{
|
|
52
45
|
type: "text",
|
|
53
|
-
text: `❌ Invalid operation: ${args.operation}. Valid operations are: ${
|
|
46
|
+
text: `❌ Invalid operation: ${args.operation}. Valid operations are: ${validOperations.join(", ")}`
|
|
54
47
|
}
|
|
55
48
|
],
|
|
56
49
|
isError: true
|
|
57
50
|
};
|
|
58
51
|
}
|
|
59
|
-
// Handle read operation
|
|
60
|
-
if (args.operation === "read") {
|
|
61
|
-
const result = await get_note_attributes(args.noteId, axiosInstance);
|
|
62
|
-
return format_attribute_response(result, args.noteId, "read");
|
|
63
|
-
}
|
|
64
52
|
// Validate attributes for write operations
|
|
65
53
|
if (!args.attributes || !Array.isArray(args.attributes) || args.attributes.length === 0) {
|
|
66
54
|
return {
|
|
@@ -106,6 +94,109 @@ export async function handleManageAttributes(args, axiosInstance, permissionChec
|
|
|
106
94
|
};
|
|
107
95
|
}
|
|
108
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Handle read_attributes MCP request
|
|
99
|
+
*/
|
|
100
|
+
export async function handleReadAttributes(args, axiosInstance, permissionChecker) {
|
|
101
|
+
try {
|
|
102
|
+
// Validate required parameters
|
|
103
|
+
if (!args.noteId) {
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: "❌ Missing required parameter: noteId"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
isError: true
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// Check READ permission
|
|
115
|
+
if (!permissionChecker.hasPermission("READ")) {
|
|
116
|
+
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to read attributes.");
|
|
117
|
+
}
|
|
118
|
+
// Execute the read operation
|
|
119
|
+
const result = await read_attributes(args, axiosInstance);
|
|
120
|
+
return format_read_attribute_response(result, args.noteId);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: `❌ Attribute read operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
isError: true
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Format read attribute operation result for MCP response
|
|
136
|
+
*/
|
|
137
|
+
function format_read_attribute_response(result, noteId) {
|
|
138
|
+
const content = [];
|
|
139
|
+
// Add status message
|
|
140
|
+
if (result.success) {
|
|
141
|
+
content.push({
|
|
142
|
+
type: "text",
|
|
143
|
+
text: `✅ ${result.message}`
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
content.push({
|
|
148
|
+
type: "text",
|
|
149
|
+
text: `❌ ${result.message}`
|
|
150
|
+
});
|
|
151
|
+
// Add error details if available
|
|
152
|
+
if (result.errors && result.errors.length > 0) {
|
|
153
|
+
content.push({
|
|
154
|
+
type: "text",
|
|
155
|
+
text: `📋 Error details:\n${result.errors.map((err, i) => `${i + 1}. ${err}`).join('\n')}`
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Add attribute data for successful operations
|
|
160
|
+
if (result.success && result.attributes && result.attributes.length > 0) {
|
|
161
|
+
// Separate labels and relations for better organization
|
|
162
|
+
const labels = result.attributes.filter(attr => attr.type === 'label');
|
|
163
|
+
const relations = result.attributes.filter(attr => attr.type === 'relation');
|
|
164
|
+
content.push({
|
|
165
|
+
type: "text",
|
|
166
|
+
text: format_attributes_for_display(result.attributes)
|
|
167
|
+
});
|
|
168
|
+
// Add structured summary if available
|
|
169
|
+
if (result.summary) {
|
|
170
|
+
content.push({
|
|
171
|
+
type: "text",
|
|
172
|
+
text: `📊 Summary: ${result.summary.total} total attributes (${result.summary.labels} labels, ${result.summary.relations} relations)`
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// Add detailed breakdown
|
|
176
|
+
if (labels.length > 0) {
|
|
177
|
+
content.push({
|
|
178
|
+
type: "text",
|
|
179
|
+
text: `🏷️ Labels (${labels.length}):\n${labels.map(attr => {
|
|
180
|
+
const value = attr.value ? ` = "${attr.value}"` : "";
|
|
181
|
+
return ` #${attr.name}${value}`;
|
|
182
|
+
}).join('\n')}`
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (relations.length > 0) {
|
|
186
|
+
content.push({
|
|
187
|
+
type: "text",
|
|
188
|
+
text: `🔗 Relations (${relations.length}):\n${relations.map(attr => ` ~${attr.name} = "${attr.value}"`).join('\n')}`
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else if (result.success) {
|
|
193
|
+
content.push({
|
|
194
|
+
type: "text",
|
|
195
|
+
text: "📋 No attributes found for this note"
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return { content };
|
|
199
|
+
}
|
|
109
200
|
/**
|
|
110
201
|
* Format attribute operation result for MCP response
|
|
111
202
|
*/
|
|
@@ -178,23 +269,27 @@ function format_attributes_for_display(attributes) {
|
|
|
178
269
|
*/
|
|
179
270
|
export function get_attributes_help() {
|
|
180
271
|
return `
|
|
181
|
-
🔧 Attribute Management
|
|
272
|
+
🔧 Attribute Management Tools
|
|
182
273
|
|
|
183
|
-
|
|
274
|
+
📖 read_attributes: Read all attributes (labels and relations) for a note
|
|
275
|
+
🔧 manage_attributes: Create, update, delete attributes (write operations)
|
|
184
276
|
|
|
185
277
|
📝 Usage Examples:
|
|
186
278
|
|
|
187
|
-
|
|
279
|
+
📖 Read Attributes:
|
|
280
|
+
- noteId: "abc123"
|
|
281
|
+
|
|
282
|
+
🔧 Create a single label:
|
|
188
283
|
- noteId: "abc123"
|
|
189
284
|
- operation: "create"
|
|
190
285
|
- attributes: [{type: "label", name: "important", position: 10}]
|
|
191
286
|
|
|
192
|
-
|
|
287
|
+
🔧 Create a template relation:
|
|
193
288
|
- noteId: "abc123"
|
|
194
289
|
- operation: "create"
|
|
195
290
|
- attributes: [{type: "relation", name: "template", value: "Board", position: 10}]
|
|
196
291
|
|
|
197
|
-
|
|
292
|
+
🔧 Create multiple attributes (batch):
|
|
198
293
|
- noteId: "abc123"
|
|
199
294
|
- operation: "batch_create"
|
|
200
295
|
- attributes: [
|
|
@@ -203,16 +298,12 @@ This tool manages note attributes (labels and relations) in TriliumNext.
|
|
|
203
298
|
{type: "relation", name: "template", value: "Grid View", position: 30}
|
|
204
299
|
]
|
|
205
300
|
|
|
206
|
-
|
|
207
|
-
- noteId: "abc123"
|
|
208
|
-
- operation: "read"
|
|
209
|
-
|
|
210
|
-
5. Update an attribute:
|
|
301
|
+
🔧 Update an attribute:
|
|
211
302
|
- noteId: "abc123"
|
|
212
303
|
- operation: "update"
|
|
213
304
|
- attributes: [{type: "label", name: "important", position: 15}]
|
|
214
305
|
|
|
215
|
-
|
|
306
|
+
🔧 Delete an attribute:
|
|
216
307
|
- noteId: "abc123"
|
|
217
308
|
- operation: "delete"
|
|
218
309
|
- attributes: [{type: "label", name: "important"}]
|
|
@@ -224,5 +315,6 @@ This tool manages note attributes (labels and relations) in TriliumNext.
|
|
|
224
315
|
- Use "batch_create" for multiple attributes (faster than individual calls)
|
|
225
316
|
- Template relations require the target note to exist in your Trilium instance
|
|
226
317
|
- Position values control display order (lower numbers appear first)
|
|
318
|
+
- Use read_attributes to view existing attributes before making changes
|
|
227
319
|
`;
|
|
228
320
|
}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
import axios from 'axios';
|
|
6
6
|
import { logVerbose, logVerboseApi, logVerboseAxiosError } from "../utils/verboseUtils.js";
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Manage note attributes with write operations (create, update, delete)
|
|
9
|
+
* This function provides write-only access to note attributes
|
|
9
10
|
*/
|
|
10
11
|
export async function manage_attributes(params, axiosInstance) {
|
|
11
12
|
try {
|
|
@@ -262,11 +263,12 @@ function validate_attribute(attribute) {
|
|
|
262
263
|
};
|
|
263
264
|
}
|
|
264
265
|
/**
|
|
265
|
-
*
|
|
266
|
+
* Read all attributes for a note (labels and relations)
|
|
267
|
+
* This function provides read-only access to note attributes
|
|
266
268
|
*/
|
|
267
|
-
export async function
|
|
269
|
+
export async function read_attributes(params, axiosInstance) {
|
|
268
270
|
try {
|
|
269
|
-
const response = await axiosInstance.get(`/notes/${noteId}`);
|
|
271
|
+
const response = await axiosInstance.get(`/notes/${params.noteId}`);
|
|
270
272
|
const attributes = response.data.attributes.map((attr) => ({
|
|
271
273
|
type: attr.type,
|
|
272
274
|
name: attr.name,
|
|
@@ -274,10 +276,20 @@ export async function get_note_attributes(noteId, axiosInstance) {
|
|
|
274
276
|
position: attr.position,
|
|
275
277
|
isInheritable: attr.isInheritable
|
|
276
278
|
}));
|
|
279
|
+
// Separate labels and relations for better organization
|
|
280
|
+
const labels = attributes.filter(attr => attr.type === 'label');
|
|
281
|
+
const relations = attributes.filter(attr => attr.type === 'relation');
|
|
277
282
|
return {
|
|
278
283
|
success: true,
|
|
279
|
-
message: `Retrieved ${attributes.length} attributes for note ${noteId}`,
|
|
280
|
-
attributes
|
|
284
|
+
message: `Retrieved ${attributes.length} attributes for note ${params.noteId} (${labels.length} labels, ${relations.length} relations)`,
|
|
285
|
+
attributes,
|
|
286
|
+
// Add structured summary for easier parsing
|
|
287
|
+
summary: {
|
|
288
|
+
total: attributes.length,
|
|
289
|
+
labels: labels.length,
|
|
290
|
+
relations: relations.length,
|
|
291
|
+
noteId: params.noteId
|
|
292
|
+
}
|
|
281
293
|
};
|
|
282
294
|
}
|
|
283
295
|
catch (error) {
|
|
@@ -321,22 +321,17 @@ function createSearchProperties() {
|
|
|
321
321
|
export function createReadAttributeTools() {
|
|
322
322
|
return [
|
|
323
323
|
{
|
|
324
|
-
name: "
|
|
325
|
-
description: "Read
|
|
324
|
+
name: "read_attributes",
|
|
325
|
+
description: "Read all attributes (labels and relations) for a note. View existing labels (#tags), template relations (~template), and note metadata. This tool provides read-only access to inspect current attributes assigned to any note. Returns structured data with labels, relations, and summary information.",
|
|
326
326
|
inputSchema: {
|
|
327
327
|
type: "object",
|
|
328
328
|
properties: {
|
|
329
329
|
noteId: {
|
|
330
330
|
type: "string",
|
|
331
331
|
description: "ID of the note to read attributes from"
|
|
332
|
-
},
|
|
333
|
-
operation: {
|
|
334
|
-
type: "string",
|
|
335
|
-
enum: ["read"],
|
|
336
|
-
description: "Operation type: 'read' (list all attributes)"
|
|
337
332
|
}
|
|
338
333
|
},
|
|
339
|
-
required: ["noteId"
|
|
334
|
+
required: ["noteId"]
|
|
340
335
|
}
|
|
341
336
|
}
|
|
342
337
|
];
|
|
@@ -348,7 +343,7 @@ export function createWriteAttributeTools() {
|
|
|
348
343
|
return [
|
|
349
344
|
{
|
|
350
345
|
name: "manage_attributes",
|
|
351
|
-
description: "Manage note attributes
|
|
346
|
+
description: "Manage note attributes with write operations (create, update, delete). Create labels (#tags), template relations (~template), update existing attributes, and organize notes with metadata. IMPORTANT: This tool only provides write access - use read_attributes to view existing attributes. Relations require values pointing to existing notes (e.g., template relations use 'Board', 'Calendar'; author relations use target note titles or IDs). UPDATE LIMITATIONS: For labels, only value and position can be updated. For relations, only position can be updated. The isInheritable property cannot be changed via update - delete and recreate to modify inheritability. Supports single operations and efficient batch creation for better performance.",
|
|
352
347
|
inputSchema: {
|
|
353
348
|
type: "object",
|
|
354
349
|
properties: {
|