manage_attributes
Create, update, or delete note attributes like labels (#tags) and relations (~connections) to organize metadata and define relationships between notes in TriliumNext.
Instructions
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.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| noteId | Yes | ID of the note to manage attributes for | |
| operation | Yes | Operation type: 'create' (new attribute), 'update' (modify existing - limited to label value/position and relation position only), 'delete' (remove attribute), 'batch_create' (multiple new attributes efficiently) | |
| attributes | No | Array of attributes to create/update/delete. Required for all write operations. IMPORTANT: Update operations have limitations - only label values/positions and relation positions can be updated. To change isInheritable or other properties, delete and recreate the attribute. |
Implementation Reference
- src/modules/attributeHandler.ts:27-124 (handler)Primary MCP tool handler for 'manage_attributes'. Validates inputs (noteId, operation, attributes), checks WRITE permission, constructs params, invokes core manage_attributes function from attributeManager, formats success/error responses with structured content blocks including emojis and details.export async function handleManageAttributes( args: ManageAttributesRequest, axiosInstance: AxiosInstance, permissionChecker: PermissionChecker ): Promise<any> { try { // Validate required parameters if (!args.noteId) { return { content: [ { type: "text", text: "❌ Missing required parameter: noteId" } ], isError: true }; } if (!args.operation) { return { content: [ { type: "text", text: "❌ Missing required parameter: operation" } ], isError: true }; } // Check WRITE permission for all manage_attributes operations if (!permissionChecker.hasPermission("WRITE")) { throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to manage attributes."); } // Validate operation const validOperations = ["create", "update", "delete", "batch_create"]; if (!validOperations.includes(args.operation)) { return { content: [ { type: "text", text: `❌ Invalid operation: ${args.operation}. Valid operations are: ${validOperations.join(", ")}` } ], isError: true }; } // Validate attributes for write operations if (!args.attributes || !Array.isArray(args.attributes) || args.attributes.length === 0) { return { content: [ { type: "text", text: "❌ Missing or invalid required parameter: attributes (must be a non-empty array for write operations)" } ], isError: true }; } // For single operations, ensure only one attribute if (["create", "update", "delete"].includes(args.operation) && args.attributes.length !== 1) { return { content: [ { type: "text", text: `❌ Operation '${args.operation}' requires exactly one attribute, but ${args.attributes.length} were provided` } ], isError: true }; } // Execute the attribute operation const params: ManageAttributesParams = { noteId: args.noteId, operation: args.operation as "create" | "update" | "delete" | "batch_create", attributes: args.attributes }; const result = await manage_attributes(params, axiosInstance); return format_attribute_response(result, args.noteId, args.operation); } catch (error) { return { content: [ { type: "text", text: `❌ Attribute operation failed: ${error instanceof Error ? error.message : 'Unknown error'}` } ], isError: true }; } }
- src/modules/toolDefinitions.ts:348-421 (registration)Defines the tool registration object for 'manage_attributes', including name, description, and full inputSchema (JSON Schema with properties: noteId, operation (create/update/delete/batch_create), attributes array with type/name/value/position/isInheritable). Returned conditionally in generateTools() if WRITE permission.export function createWriteAttributeTools(): any[] { return [ { name: "manage_attributes", 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.", inputSchema: { type: "object", properties: { noteId: { type: "string", description: "ID of the note to manage attributes for" }, operation: { type: "string", enum: ["create", "update", "delete", "batch_create"], description: "Operation type: 'create' (new attribute), 'update' (modify existing - limited to label value/position and relation position only), 'delete' (remove attribute), 'batch_create' (multiple new attributes efficiently)" }, attributes: { type: "array", description: "Array of attributes to create/update/delete. Required for all write operations. IMPORTANT: Update operations have limitations - only label values/positions and relation positions can be updated. To change isInheritable or other properties, delete and recreate the attribute.", items: { type: "object", properties: { type: { type: "string", enum: ["label", "relation"], description: "Attribute type: 'label' for #tags (categories, metadata), 'relation' for ~connections (template, author, etc.)" }, name: { type: "string", description: "Attribute name: for labels use descriptive tags like 'status', 'priority', 'project'; for relations use connection types that define relationships between notes (e.g., 'template' for built-in templates, 'author' for content creators, 'publisher' for publications). Relations connect notes and always require a target note value." }, value: { type: "string", description: "Attribute value: REQUIRED for relations (relations must point to existing notes - use template names like 'Board', 'Calendar', 'Text Snippet' or target note IDs/titles like 'Tolkien' or 'abc123def'), optional for labels (e.g., status labels like 'In Progress', priority labels like 'High'). Relations always need values since they connect notes together." }, position: { type: "number", description: "Display position (lower numbers appear first, default: 10)", default: 10 }, isInheritable: { type: "boolean", description: "Whether attribute is inherited by child notes (default: false). NOTE: This property cannot be changed via update operations. To modify inheritability, delete and recreate the attribute.", default: false } }, required: ["type", "name"] } } }, required: ["noteId", "operation"], dependencies: { operation: { oneOf: [ { properties: { operation: { enum: ["create", "update", "delete"] }, attributes: { minItems: 1, maxItems: 1 } } }, { properties: { operation: { enum: ["batch_create"] }, attributes: { minItems: 1 } } } ] } } } } ]; }
- src/utils/validationUtils.ts:25-29 (schema)Zod schema for validating manage_attributes request parameters, used potentially in validation pipelines (includes attributeSchema for nested validation). Note: enum includes 'read' not in tool.export const manageAttributesSchema = z.object({ noteId: z.string().min(1, 'Note ID cannot be empty'), operation: z.enum(['create', 'update', 'delete', 'batch_create', 'read']), attributes: z.array(attributeSchema).optional() });
- Core helper function implementing the attribute CRUD logic. Dispatches to specific create/update/delete/batch_create functions that interact with Trilium ETAPI (/attributes POST/PATCH/DELETE), handles validation, parallel batch ops, finds attributes by name/type for update/delete.export async function manage_attributes( params: ManageAttributesParams, axiosInstance: AxiosInstance ): Promise<AttributeOperationResult> { try { switch (params.operation) { case "create": return await create_single_attribute(params.noteId, params.attributes[0], axiosInstance); case "batch_create": return await create_batch_attributes(params.noteId, params.attributes, axiosInstance); case "update": return await update_attribute(params.noteId, params.attributes[0], axiosInstance); case "delete": return await delete_attribute(params.noteId, params.attributes[0], axiosInstance); default: return { success: false, message: `Unsupported operation: ${params.operation}`, errors: [`Operation ${params.operation} is not supported`] }; } } catch (error) { return { success: false, message: `Attribute operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, errors: [error instanceof Error ? error.message : 'Unknown error'] }; } }
- src/index.ts:77-83 (registration)MCP server ListTools handler that dynamically generates tool list including manage_attributes via generateTools(), which includes createWriteAttributeTools() based on permissions.this.server.setRequestHandler(ListToolsRequestSchema, async () => { // Generate standard tools based on permissions const tools = generateTools(this); return { tools }; });
- src/index.ts:118-119 (handler)Central dispatch switch in CallToolRequest handler routes 'manage_attributes' calls to handleManageAttributes.case "manage_attributes": return await handleManageAttributes(request.params.arguments as any, this.axiosInstance, this);