Skip to main content
Glama

resolve_note_id

Convert note or folder names into their corresponding IDs for use with other tools in the TriliumNext Notes system, enabling accurate identification when users reference notes by title rather than ID.

Instructions

Resolves a note/folder name to its actual note ID for use with other tools. You MUST call this function when users provide note names instead of note IDs (e.g., 'wqd7006', 'My Project') UNLESS the user explicitly provides a note ID. Simple title-based search with user choice when multiple matches found.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
noteNameYesName or title of the note to find (e.g., 'wqd7006', 'My Project Folder')
exactMatchNoWhether to require exact title match. RECOMMENDED: Use false (default) for best user experience - fuzzy search finds partial matches and handles typos, while still prioritizing exact matches when found. Only set to true when user explicitly requests exact matching.
maxResultsNoMaximum number of results to return in topMatches array (default: 10)
autoSelectNoWhen multiple matches found: true = auto-select best match (current behavior), false = stop and ask user to choose from alternatives (default: false for better user experience)

Implementation Reference

  • Core handler function that implements the resolve_note_id tool logic: performs title-based search, handles multiple matches with prioritization (exact, folders, recent), auto-selects or requires user choice.
    export async function handleResolveNoteId( args: ResolveNoteOperation, axiosInstance: any ): Promise<ResolveNoteResponse> { const { noteName, exactMatch = false, maxResults = 3, autoSelect = false } = args; // Verbose logging logVerboseInput("resolve_note_id", args); if (!noteName?.trim()) { throw new Error("Note name must be provided"); } // Simple title-based search criteria const searchCriteria: any[] = [{ property: "title", type: "noteProperty", op: exactMatch ? "=" : "contains", value: noteName.trim() }]; // Build search query from simple criteria const searchParams: SearchOperation = { searchCriteria }; logVerboseInput("resolve_note_id", searchParams); // Search for notes matching the criteria const query = buildSearchQuery(searchParams); const params = new URLSearchParams(); params.append("search", query); params.append("fastSearch", "false"); params.append("includeArchivedNotes", "true"); const response = await axiosInstance.get(`/notes?${params.toString()}`); let searchResults = response.data.results || []; const totalMatches = searchResults.length; if (searchResults.length === 0) { // Enhanced fallback suggestions when no matches found let nextSteps = "No notes found matching the search criteria."; // Primary fallback: suggest broader search using search_notes nextSteps += ` Consider using search_notes for broader results: search_notes(text: "${noteName.trim()}") to find notes containing "${noteName.trim()}" in title or content.`; return { noteId: null, title: null, found: false, matches: 0, nextSteps }; } // Store original results for top matches const originalResults = [...searchResults]; // Check if user choice is required (multiple matches and autoSelect is false) if (totalMatches > 1 && !autoSelect) { // Simple prioritization: exact matches → folders → most recent const topMatches = originalResults .sort((a: any, b: any) => { // First priority: exact title matches const aExact = a.title && a.title.toLowerCase() === noteName.toLowerCase(); const bExact = b.title && b.title.toLowerCase() === noteName.toLowerCase(); if (aExact && !bExact) return -1; if (!aExact && bExact) return 1; // Second priority: book type (folders) if (a.type === 'book' && b.type !== 'book') return -1; if (a.type !== 'book' && b.type === 'book') return 1; // Final priority: most recent return new Date(b.dateModified).getTime() - new Date(a.dateModified).getTime(); }) .slice(0, maxResults) .map((note: any) => ({ noteId: note.noteId, title: note.title, type: note.type, dateModified: note.dateModified })); return { noteId: null, title: null, found: true, matches: totalMatches, requiresUserChoice: true, topMatches }; } // Apply simple prioritization for selecting the best match if (searchResults.length > 1) { searchResults.sort((a: any, b: any) => { // First priority: exact title matches (if not exactMatch mode, prefer exact matches) if (!exactMatch) { const aExact = a.title && a.title.toLowerCase() === noteName.toLowerCase(); const bExact = b.title && b.title.toLowerCase() === noteName.toLowerCase(); if (aExact && !bExact) return -1; if (!aExact && bExact) return 1; } // Second priority: book type (folders) if (a.type === 'book' && b.type !== 'book') return -1; if (a.type !== 'book' && b.type === 'book') return 1; // Final priority: most recent return new Date(b.dateModified).getTime() - new Date(a.dateModified).getTime(); }); } // Return the first match (or best match if multiple) const selectedNote = searchResults[0]; // Prepare top N matches for display (from original results, with simple prioritization) const topMatches = originalResults .sort((a: any, b: any) => { // Simple prioritization: exact matches → folders → most recent const aExact = a.title && a.title.toLowerCase() === noteName.toLowerCase(); const bExact = b.title && b.title.toLowerCase() === noteName.toLowerCase(); if (aExact && !bExact) return -1; if (!aExact && bExact) return 1; if (a.type === 'book' && b.type !== 'book') return -1; if (a.type !== 'book' && b.type === 'book') return 1; return new Date(b.dateModified).getTime() - new Date(a.dateModified).getTime(); }) .slice(0, maxResults) .map((note: any) => ({ noteId: note.noteId, title: note.title, type: note.type, dateModified: note.dateModified })); return { noteId: selectedNote.noteId, title: selectedNote.title, found: true, matches: totalMatches, topMatches }; }
  • Tool request handler for resolve_note_id: validates READ permission, calls core handleResolveNoteId, formats responses including user choice prompts for multiple matches.
    export async function handleResolveNoteRequest(args: any, permissionChecker: any, axiosInstance: any): Promise<any> { // Permission check if (!permissionChecker.hasPermission("READ")) { throw new Error("READ permission required for resolve_note_id operation"); } const resolveOperation: ResolveNoteOperation = { noteName: args.noteName, exactMatch: args.exactMatch, maxResults: args.maxResults, autoSelect: args.autoSelect }; const response = await handleResolveNoteId(resolveOperation, axiosInstance); // Enhanced response formatting for multiple matches if (response.requiresUserChoice && response.topMatches) { const choiceList = response.topMatches .map((match, index) => `${index + 1}. ${match.title} (ID: ${match.noteId}, Type: ${match.type}, Modified: ${match.dateModified})`) .join('\n'); return { content: [ { type: "text", text: `Found ${response.matches} matches for "${resolveOperation.noteName}". Please choose:\n\n${choiceList}\n\nTo select: Use the note ID directly, or specify autoSelect=true for automatic selection, or refine your search criteria.` } ] }; } // Standard response for single match or auto-selected if (response.found && response.noteId) { return { content: [ { type: "text", text: `✅ Resolved "${resolveOperation.noteName}" to Note ID: ${response.noteId} (Title: "${response.title}", Matches: ${response.matches})` } ] }; } // No matches found return { content: [ { type: "text", text: `❌ No notes found matching "${resolveOperation.noteName}". ${response.nextSteps || "Try a different search term or check spelling."}` } ] }; }
  • Input schema and description for the resolve_note_id tool, including parameters like noteName, exactMatch, maxResults, autoSelect.
    name: "resolve_note_id", description: "Resolves a note/folder name to its actual note ID for use with other tools. You MUST call this function when users provide note names instead of note IDs (e.g., 'wqd7006', 'My Project') UNLESS the user explicitly provides a note ID. Simple title-based search with user choice when multiple matches found.", inputSchema: { type: "object", properties: { noteName: { type: "string", description: "Name or title of the note to find (e.g., 'wqd7006', 'My Project Folder')", }, exactMatch: { type: "boolean", description: "Whether to require exact title match. RECOMMENDED: Use false (default) for best user experience - fuzzy search finds partial matches and handles typos, while still prioritizing exact matches when found. Only set to true when user explicitly requests exact matching.", default: false }, maxResults: { type: "number", description: "Maximum number of results to return in topMatches array (default: 10)", default: 10, minimum: 1, maximum: 10 }, autoSelect: { type: "boolean", description: "When multiple matches found: true = auto-select best match (current behavior), false = stop and ask user to choose from alternatives (default: false for better user experience)", default: false } }, required: ["noteName"], }, },
  • src/index.ts:112-113 (registration)
    MCP server router registration: dispatches resolve_note_id tool calls to the handleResolveNoteRequest function.
    case "resolve_note_id": return await handleResolveNoteRequest(request.params.arguments, this, this.axiosInstance);
  • TypeScript interfaces defining input (ResolveNoteOperation) and output (ResolveNoteResponse) for the resolve_note_id handler.
    export interface ResolveNoteOperation { noteName: string; exactMatch?: boolean; maxResults?: number; autoSelect?: boolean; } export interface ResolveNoteResponse { noteId: string | null; title: string | null; found: boolean; matches: number; requiresUserChoice?: boolean; topMatches?: Array<{ noteId: string; title: string; type: string; dateModified: string; }>; nextSteps?: string; }

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