search_notes
Find specific notes in TriliumNext using fulltext or structured queries. Specify a search term and limit results to streamline your knowledge base navigation.
Instructions
Search for notes in TriliumNext
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results to return | |
| query | Yes | Search query (fulltext or structured, max 500 characters) |
Implementation Reference
- src/tools/search-notes.js:5-144 (handler)Core handler function that validates input, queries TriliumNext API for notes matching the search query, formats results with structured JSON, and handles various error cases with detailed responses.export async function searchNotes(triliumClient, args) { try { // Validate inputs const query = validators.searchQuery(args.query); const limit = validators.limit(args.limit); logger.debug(`Searching notes: query="${query}", limit=${limit}`); // Prepare search parameters for TriliumNext API const params = new URLSearchParams({ search: query, limit: limit.toString(), }); // Search notes via TriliumNext API const response = await triliumClient.get(`notes?${params}`); if (!response || typeof response !== 'object' || !Array.isArray(response.results)) { throw new TriliumAPIError('Invalid response from TriliumNext API - expected object with results array'); } const results = response.results; logger.info(`Found ${results.length} notes matching query "${query}"`); // Prepare structured response data const searchData = { query, limit, totalResults: results.length, hasMore: results.length === limit, // Might be more results if we hit the limit timestamp: new Date().toISOString(), notes: results.map(note => ({ noteId: note.noteId, title: note.title || 'Untitled', type: note.type || 'text', dateCreated: note.dateCreated, dateModified: note.dateModified, parentNoteIds: note.parentNoteIds || [], isProtected: note.isProtected || false, // Preserve additional fields that might be useful ...(note.mime && { mime: note.mime }), ...(note.attributes && { attributes: note.attributes }), ...(note.contentLength && { contentLength: note.contentLength }) })) }; if (results.length === 0) { return { content: [ { type: 'text', text: `No notes found for "${query}"` }, { type: 'application/json', text: JSON.stringify(searchData, null, 2) } ], }; } // Create concise summary const summary = `Found ${results.length} note${results.length === 1 ? '' : 's'} for "${query}"${searchData.hasMore ? ' (showing first ' + limit + ')' : ''}`; return { content: [ { type: 'text', text: summary }, { type: 'application/json', text: JSON.stringify(searchData, null, 2) } ], }; } catch (error) { logger.error(`Failed to search notes: ${error.message}`); // Create structured error response const errorData = { query: args.query, limit: args.limit, timestamp: new Date().toISOString(), error: { type: error.constructor.name, message: error.message, ...(error instanceof TriliumAPIError && { status: error.status }), ...(error instanceof TriliumAPIError && error.details && { details: error.details }) } }; if (error instanceof ValidationError) { return { content: [ { type: 'text', text: `Validation error: ${error.message}` }, { type: 'application/json', text: JSON.stringify(errorData, null, 2) } ], isError: true, }; } if (error instanceof TriliumAPIError) { return { content: [ { type: 'text', text: `TriliumNext API error: ${error.message}` }, { type: 'application/json', text: JSON.stringify(errorData, null, 2) } ], isError: true, }; } // Unknown error return { content: [ { type: 'text', text: `Search failed: ${error.message}` }, { type: 'application/json', text: JSON.stringify(errorData, null, 2) } ], isError: true, }; } }
- src/index.js:85-100 (schema)MCP input schema defining the expected arguments for the search_notes tool: required 'query' string and optional 'limit' number.inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (fulltext or structured, max 500 characters)', }, limit: { type: 'number', minimum: 1, maximum: 100, default: 10, description: 'Maximum number of results to return', }, }, required: ['query'],
- src/index.js:146-147 (registration)Registration in the tool dispatch switch statement that routes 'search_notes' calls to the wrapper method.case 'search_notes': return await this.searchNotes(request.params.arguments);
- src/index.js:82-101 (registration)Tool metadata registration in the ListTools response, including name, description, and schema.{ name: 'search_notes', description: 'Search for notes in TriliumNext', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (fulltext or structured, max 500 characters)', }, limit: { type: 'number', minimum: 1, maximum: 100, default: 10, description: 'Maximum number of results to return', }, }, required: ['query'], },
- src/utils/validation.js:54-66 (schema)Internal validator function for the search query parameter, enforcing non-empty string up to 500 characters.searchQuery: (query) => { if (!query || typeof query !== 'string') { throw new ValidationError('Search query must be a non-empty string'); } const trimmed = query.trim(); if (trimmed.length === 0) { throw new ValidationError('Search query cannot be empty'); } if (trimmed.length > 500) { throw new ValidationError('Search query cannot exceed 500 characters'); } return trimmed; },