BareGit

Trilium: fix note creation.

Author: MetroWind <chris.corsair@gmail.com>
Date: Tue Feb 24 14:05:20 2026 -0800
Commit: 688f2f9c1eb8eec1cfb56bbf35685ec397fcd179

Changes

diff --git a/skills/trilium/SKILL.md b/skills/trilium/SKILL.md
index b88aed6..479fbaa 100644
--- a/skills/trilium/SKILL.md
+++ b/skills/trilium/SKILL.md
@@ -44,3 +44,12 @@ Keep your notes up to date or capture new information.
 
 ## Notes on HTML Content
 Trilium stores notes as HTML. When creating or updating notes, ensure the content is wrapped in appropriate HTML tags (e.g., `<p>`, `<ul>`, `<li>`) for best rendering in the Trilium UI.
+
+**Important formatting rules:**
+1.  **Do NOT** include the note's title inside the HTML content (e.g., as an `<h1>`). Trilium displays the title separately at the top of the UI.
+2.  Use semantic tags for sections, but start from `<h2>` or below if sub-headers are needed.
+
+## Templates and Attributes
+You can create notes that inherit from a specific template and set attributes in a single call.
+- **Template**: Use `templateNoteId` in `create_note`.
+- **Attributes**: Use the `attributes` array in `create_note` or `create_attribute` separately for setting labels and relations.
diff --git a/skills/trilium/mcp/src/handlers.py b/skills/trilium/mcp/src/handlers.py
index 188fd2f..77bede8 100644
--- a/skills/trilium/mcp/src/handlers.py
+++ b/skills/trilium/mcp/src/handlers.py
@@ -55,18 +55,48 @@ class ToolHandlers:
             },
             {
                 "name": "create_note",
-                "description": "Create a new note in Trilium.",
+                "description": "Create a new note in Trilium. Can optionally specify a template and attributes.",
                 "inputSchema": {
                     "type": "object",
                     "properties": {
                         "parentNoteId": {"type": "string", "description": "ID of parent note (default 'root')."},
                         "title": {"type": "string", "description": "Title of the note."},
                         "content": {"type": "string", "description": "HTML content for the note."},
-                        "type": {"type": "string", "description": "Type of note (e.g., 'text').", "default": "text"}
+                        "type": {"type": "string", "description": "Type of note (e.g., 'text').", "default": "text"},
+                        "templateNoteId": {"type": "string", "description": "Optional: ID of a template note to inherit from."},
+                        "attributes": {
+                            "type": "array",
+                            "description": "Optional: List of attributes (labels or relations) to add.",
+                            "items": {
+                                "type": "object",
+                                "properties": {
+                                    "type": {"type": "string", "enum": ["label", "relation"]},
+                                    "name": {"type": "string"},
+                                    "value": {"type": "string"},
+                                    "isInheritable": {"type": "boolean", "default": False}
+                                },
+                                "required": ["type", "name", "value"]
+                            }
+                        }
                     },
                     "required": ["title", "content"]
                 }
             },
+            {
+                "name": "create_attribute",
+                "description": "Create a new attribute (label or relation) for a note.",
+                "inputSchema": {
+                    "type": "object",
+                    "properties": {
+                        "noteId": {"type": "string", "description": "ID of the note to add the attribute to."},
+                        "type": {"type": "string", "enum": ["label", "relation"]},
+                        "name": {"type": "string", "description": "Name of the attribute."},
+                        "value": {"type": "string", "description": "Value of the attribute."},
+                        "isInheritable": {"type": "boolean", "description": "Whether the attribute is inheritable.", "default": False}
+                    },
+                    "required": ["noteId", "type", "name", "value"]
+                }
+            },
             {
                 "name": "update_note_content",
                 "description": "Update the content of an existing note.",
@@ -93,12 +123,39 @@ class ToolHandlers:
             return self.client.getNoteChildren(params["noteId"])
         elif name == "create_note":
             parent_note_id = params.get("parentNoteId", "root")
-            return self.client.createNote(
+            note_data = self.client.createNote(
                 parent_note_id,
                 params["title"],
                 params["content"],
                 params.get("type", "text")
             )
+            note_id = note_data["note"]["noteId"]
+
+            # Handle optional template inheritance
+            if params.get("templateNoteId"):
+                self.client.createAttribute(
+                    note_id, "relation", "template", params["templateNoteId"]
+                )
+
+            # Handle additional attributes
+            if params.get("attributes"):
+                for attr in params["attributes"]:
+                    self.client.createAttribute(
+                        note_id,
+                        attr["type"],
+                        attr["name"],
+                        attr["value"],
+                        attr.get("isInheritable", False)
+                    )
+            return note_data
+        elif name == "create_attribute":
+            return self.client.createAttribute(
+                params["noteId"],
+                params["type"],
+                params["name"],
+                params["value"],
+                params.get("isInheritable", False)
+            )
         elif name == "update_note_content":
             self.client.updateNoteContent(params["noteId"], params["content"])
             return f"Successfully updated note content for {params['noteId']}."
diff --git a/skills/trilium/mcp/src/trilium_client.py b/skills/trilium/mcp/src/trilium_client.py
index 460e271..64b006b 100644
--- a/skills/trilium/mcp/src/trilium_client.py
+++ b/skills/trilium/mcp/src/trilium_client.py
@@ -49,12 +49,19 @@ class TriliumClient:
 
     def getNoteChildren(self, note_id: str) -> List[Dict[str, Any]]:
         """List children of a specified note."""
-        # ETAPI pattern to list notes with a specific parent
+        # Note: In some versions, parentNoteId must be explicitly in query or the search query
         response = requests.get(
             f"{self.url}/etapi/notes",
             params={"parentNoteId": note_id},
             headers=self._get_headers()
         )
+        if response.status_code == 400:
+             # Fallback to searching for children if direct query fails
+             response = requests.get(
+                f"{self.url}/etapi/notes",
+                params={"search": f"parent.noteId = '{note_id}'"},
+                headers=self._get_headers()
+            )
         response.raise_for_status()
         return response.json()
 
@@ -66,8 +73,26 @@ class TriliumClient:
             "content": content,
             "type": type
         }
+        # Note: Using /etapi/create-note instead of /etapi/notes for compatibility
         response = requests.post(
-            f"{self.url}/etapi/notes",
+            f"{self.url}/etapi/create-note",
+            json=payload,
+            headers=self._get_headers("application/json")
+        )
+        response.raise_for_status()
+        return response.json()
+
+    def createAttribute(self, note_id: str, type: str, name: str, value: str, is_inheritable: bool = False) -> Dict[str, Any]:
+        """Create a new attribute (label or relation) for a note."""
+        payload = {
+            "noteId": note_id,
+            "type": type,
+            "name": name,
+            "value": value,
+            "isInheritable": is_inheritable
+        }
+        response = requests.post(
+            f"{self.url}/etapi/attributes",
             json=payload,
             headers=self._get_headers("application/json")
         )