Skip to main content
Glama

create_note

Create new notes in TriliumNext with duplicate title detection, handling text, code, files, and specialized note types while managing conflicts.

Instructions

Create a new note in TriliumNext with duplicate title detection. When a note with the same title already exists in the same directory, you'll be presented with choices: skip creation, create anyway (with forceCreate: true), or update the existing note. ONLY use this tool when the user explicitly requests note creation (e.g., 'create a note', 'make a new note'). DO NOT use this tool proactively or when the user is only asking questions about their notes. TIP: For code notes, content is plain text (no HTML processing).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
parentNoteIdYesID of the parent noteroot
titleYesTitle of the note
contentNoContent of the note (optional). Content requirements by note type: TEXT notes require HTML content (plain text auto-wrapped in <p> tags, e.g., '<p>Hello world</p>', '<strong>bold</strong>'); CODE/MERMAID notes require plain text ONLY (HTML tags rejected, e.g., 'def fibonacci(n):'); ⚠️ OMIT CONTENT for: 1) FILE notes (binary content uploaded separately via fileUri parameter), 2) WEBVIEW notes (use #webViewSrc label instead), 3) Container templates (Board, Calendar, Grid View, List View, Table, Geo Map), 4) System notes: RENDER (create child HTML note with type='code' and mime='application/x-html', then link with ~renderNote relation), SEARCH (queries in search properties), RELATION_MAP (visual maps), NOTE_MAP (visual hierarchies), BOOK (container notes) - these must be EMPTY to work properly. When omitted, note will be created with empty content.
typeYesType of note (aligned with TriliumNext ETAPI specification). For file uploads: Use 'image' for Images (JPG/JPEG/PNG/WebP), 'file' for Documents & Audio (PDF/DOCX/MP3/WAV/M4A). Other types: 'text', 'code', 'render', 'search', 'relationMap', 'book', 'noteMap', 'mermaid', 'webView'.
mimeNoMIME type for code/file/image notes. For file uploads, auto-detected from file extension when not specified. Supported: application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document, audio/mpeg, audio/wav, audio/mp4, image/jpg, image/png, image/webp
fileUriNoFile data source (required when type='file' or type='image'). Supports: 1) Local file path: '/path/to/document.pdf', 2) Base64 data URI: 'data:application/pdf;base64,JVBERi0xLjcK...', 3) Raw base64 string. Supports PDF, DOCX, PPTX, XLSX, CSV, MP3, WAV, M4A, JPG, JPEG, PNG, WebP formats. File will be uploaded via Trilium's two-step process: create note metadata, then upload binary content.
attributesNoOptional attributes to create with the note (labels and relations). Enables one-step note creation with metadata. Labels use #tag format (e.g., 'important', 'project'), relations connect to other notes (e.g., template relations use 'Board', 'Calendar', 'Text Snippet'). ⚠️ TEMPLATE RESTRICTIONS: Container templates (Board, Calendar, Grid View, List View, Table, Geo Map) MUST be empty notes - add content as child notes.
forceCreateNoBypass duplicate title check and create note even if a note with the same title already exists in the same directory. Use this when you want to intentionally create duplicate notes.

Implementation Reference

  • Primary MCP tool handler for 'create_note'. Validates permissions and parameters, constructs NoteOperation object, calls core handleCreateNote function, and formats MCP response.
    export async function handleCreateNoteRequest( args: any, axiosInstance: any, permissionChecker: PermissionChecker ): Promise<{ content: Array<{ type: string; text: string }> }> { if (!permissionChecker.hasPermission("WRITE")) { throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Not authorized to create notes."); } // Validate file upload requirements if (args.type === 'file' || args.type === 'image') { if (!args.fileUri) { throw new McpError( ErrorCode.InvalidParams, `Parameter 'fileUri' is required when type='${args.type}'.` ); } } // For non-file/image notes, fileUri should not be provided if (args.fileUri && !['file', 'image'].includes(args.type)) { throw new McpError( ErrorCode.InvalidParams, "Parameter 'fileUri' can only be used when type='file' or type='image'." ); } // Validate file upload requirements if (args.type === 'file' || args.type === 'image') { if (!args.fileUri) { throw new McpError( ErrorCode.InvalidParams, `Parameter 'fileUri' is required when type='${args.type}'.` ); } } // For non-file/image notes, fileUri should not be provided if (args.fileUri && !['file', 'image'].includes(args.type)) { throw new McpError( ErrorCode.InvalidParams, "Parameter 'fileUri' can only be used when type='file' or type='image'." ); } try { const noteOperation: NoteOperation = { parentNoteId: args.parentNoteId || "root", // Use default value if not provided title: args.title, type: args.type, content: args.content, mime: args.mime, fileUri: args.fileUri, attributes: args.attributes }; const result = await handleCreateNote(noteOperation, axiosInstance); return { content: [{ type: "text", text: result.message }] }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InvalidParams, error instanceof Error ? error.message : String(error)); } }
  • Core implementation logic for creating notes. Handles duplicate title detection, content validation and processing based on note type, file uploads via FileManager, Trilium ETAPI calls (POST /create-note), and attribute creation.
    export async function handleCreateNote( args: NoteOperation, axiosInstance: any ): Promise<NoteCreateResponse> { const { parentNoteId, title, type, content: rawContent, mime, fileUri, attributes, forceCreate = false } = args; // Validate required parameters if (!parentNoteId || !title || !type) { throw new Error("parentNoteId, title, and type are required for create operation."); } // Handle file uploads (both 'file' and 'image' types) if (type === 'file' || type === 'image') { // Import FileManager and utils only when needed const { FileManager } = await import('./fileManager.js'); // Validate file if provided if (!fileUri) { throw new Error(`fileUri is required when type='${type}'.`); } // Use FileManager to handle the upload const fileManager = new FileManager(axiosInstance); try { const fileResult = await fileManager.createFileNote({ parentNoteId, filePath: fileUri, title: title, mimeType: mime, attributes, noteType: type as 'file' | 'image' }); return { noteId: fileResult.note.noteId, message: `Created file note: ${fileResult.note.noteId} (${fileResult.note.title})`, duplicateFound: false }; } catch (error) { throw new Error(`File upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // Check for duplicate title in the same directory (unless forceCreate is true) if (!forceCreate) { const duplicateCheck = await checkDuplicateTitleInDirectory(parentNoteId, title, axiosInstance); if (duplicateCheck.found) { return { message: `Found existing note with title "${title}" in this directory. Please choose how to proceed:`, duplicateFound: true, duplicateNoteId: duplicateCheck.duplicateNoteId, choices: { skip: "Skip creation - do nothing", createAnyway: "Create anyway - create duplicate note with same title (set forceCreate: true)", updateExisting: "Update existing - replace content of existing note with new content" }, nextSteps: `Please specify your choice by calling create_note again with your preferred action. To update the existing note, use update_note with noteId: ${duplicateCheck.duplicateNoteId}` }; } } // Process content to ETAPI format // Content is optional - if not provided, default to empty string const content = rawContent || ""; // Extract template relation for content validation const templateRelation = extractTemplateRelation(attributes); // Validate content with template-aware rules const contentValidation = await validateContentForNoteType( content, type as NoteType, undefined, templateRelation ); if (!contentValidation.valid) { return { message: `CONTENT_VALIDATION_ERROR: ${contentValidation.error}`, duplicateFound: false, nextSteps: "Please adjust your content according to the requirements and try again." }; } // Use validated content (may have been auto-corrected) const validatedContent = contentValidation.content; // Process content to ETAPI format const processed = await processContentArray(validatedContent, type); if (processed.error) { throw new Error(`Content processing error: ${processed.error}`); } const processedContent = processed.content; // Create note with processed content (empty for file/image-only notes) const noteData: any = { parentNoteId, title, type, content: processedContent }; // Add MIME type if specified if (mime) { noteData.mime = mime; } const response = await axiosInstance.post("/create-note", noteData); const noteId = response.data.note.noteId; // Handle attributes if provided if (attributes && attributes.length > 0) { try { logVerbose("handleCreateNote", `Creating ${attributes.length} attributes for note ${noteId}`, attributes); await createNoteAttributes(noteId, attributes, axiosInstance); logVerbose("handleCreateNote", `Successfully created all attributes for note ${noteId}`); } catch (attributeError) { const errorMsg = `Note created but attributes failed to apply: ${attributeError instanceof Error ? attributeError.message : attributeError}`; logVerboseError("handleCreateNote", attributeError); console.warn(errorMsg); } } return { noteId: noteId, message: `Created note: ${noteId}`, duplicateFound: false }; }
  • Input schema and description definition for the 'create_note' tool, including all parameters, validation rules, and usage instructions.
    name: "create_note", description: "Create a new note in TriliumNext with duplicate title detection. When a note with the same title already exists in the same directory, you'll be presented with choices: skip creation, create anyway (with forceCreate: true), or update the existing note. ONLY use this tool when the user explicitly requests note creation (e.g., 'create a note', 'make a new note'). DO NOT use this tool proactively or when the user is only asking questions about their notes. TIP: For code notes, content is plain text (no HTML processing).", inputSchema: { type: "object", properties: { parentNoteId: { type: "string", description: "ID of the parent note", default: "root" }, title: { type: "string", description: "Title of the note", }, content: { type: "string", description: "Content of the note (optional). Content requirements by note type: TEXT notes require HTML content (plain text auto-wrapped in <p> tags, e.g., '<p>Hello world</p>', '<strong>bold</strong>'); CODE/MERMAID notes require plain text ONLY (HTML tags rejected, e.g., 'def fibonacci(n):'); ⚠️ OMIT CONTENT for: 1) FILE notes (binary content uploaded separately via fileUri parameter), 2) WEBVIEW notes (use #webViewSrc label instead), 3) Container templates (Board, Calendar, Grid View, List View, Table, Geo Map), 4) System notes: RENDER (create child HTML note with type='code' and mime='application/x-html', then link with ~renderNote relation), SEARCH (queries in search properties), RELATION_MAP (visual maps), NOTE_MAP (visual hierarchies), BOOK (container notes) - these must be EMPTY to work properly. When omitted, note will be created with empty content." }, type: { type: "string", enum: ["text", "code", "render", "search", "relationMap", "book", "noteMap", "mermaid", "webView", "file", "image"], description: "Type of note (aligned with TriliumNext ETAPI specification). For file uploads: Use 'image' for Images (JPG/JPEG/PNG/WebP), 'file' for Documents & Audio (PDF/DOCX/MP3/WAV/M4A). Other types: 'text', 'code', 'render', 'search', 'relationMap', 'book', 'noteMap', 'mermaid', 'webView'.", }, mime: { type: "string", description: "MIME type for code/file/image notes. For file uploads, auto-detected from file extension when not specified. Supported: application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document, audio/mpeg, audio/wav, audio/mp4, image/jpg, image/png, image/webp" }, fileUri: { type: "string", description: "File data source (required when type='file' or type='image'). Supports: 1) Local file path: '/path/to/document.pdf', 2) Base64 data URI: 'data:application/pdf;base64,JVBERi0xLjcK...', 3) Raw base64 string. Supports PDF, DOCX, PPTX, XLSX, CSV, MP3, WAV, M4A, JPG, JPEG, PNG, WebP formats. File will be uploaded via Trilium's two-step process: create note metadata, then upload binary content." }, attributes: { type: "array", description: "Optional attributes to create with the note (labels and relations). Enables one-step note creation with metadata. Labels use #tag format (e.g., 'important', 'project'), relations connect to other notes (e.g., template relations use 'Board', 'Calendar', 'Text Snippet'). ⚠️ TEMPLATE RESTRICTIONS: Container templates (Board, Calendar, Grid View, List View, Table, Geo Map) MUST be empty notes - add content as child notes.", items: { type: "object", properties: { type: { type: "string", enum: ["label", "relation"], description: "Attribute type: 'label' for #tags, 'relation' for ~connections" }, name: { type: "string", description: "Attribute name: for labels use descriptive tags, for relations use connection types (e.g., 'template', 'author')" }, value: { type: "string", description: "Attribute value: optional for labels (e.g., 'In Progress'), required for relations (e.g., 'Board', 'Calendar', target note IDs/titles)" }, 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)", default: false } }, required: ["type", "name"] } }, forceCreate: { type: "boolean", description: "Bypass duplicate title check and create note even if a note with the same title already exists in the same directory. Use this when you want to intentionally create duplicate notes.", default: false }, }, required: ["parentNoteId", "title", "type"], }, },
  • src/index.ts:93-94 (registration)
    MCP server switch statement registration: routes 'create_note' tool calls to the handleCreateNoteRequest function.
    case "create_note": return await handleCreateNoteRequest(request.params.arguments, this.axiosInstance, this);
  • src/index.ts:77-82 (registration)
    MCP ListTools handler that dynamically generates tool list including 'create_note' schema via generateTools (permission-based).
    this.server.setRequestHandler(ListToolsRequestSchema, async () => { // Generate standard tools based on permissions const tools = generateTools(this); return { tools };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tan-yong-sheng/triliumnext-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server