search_notes
Find notes in your TriliumNext knowledge base using text search queries to locate specific information quickly.
Instructions
Search for notes in TriliumNext
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query (fulltext or structured, max 500 characters) | |
| limit | No | Maximum number of results to return |
Implementation Reference
- src/tools/search-notes.js:5-144 (handler)The core implementation of the search_notes tool handler. Validates arguments, queries the TriliumNext API for notes matching the search query, processes the results into structured data, and returns both a text summary and JSON response.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:82-102 (registration)Registration of the 'search_notes' tool in the ListTools response, including name, description, and MCP input schema definition.{ 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-80 (schema)Internal validation functions for search_notes input parameters (searchQuery and limit), enforcing constraints matching the MCP schema.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; }, limit: (limit) => { if (limit === undefined || limit === null) { return 10; // default } const num = parseInt(limit, 10); if (isNaN(num) || num < 1) { throw new ValidationError('Limit must be a positive integer'); } if (num > 100) { throw new ValidationError('Limit cannot exceed 100'); } return num; }
- src/index.js:146-147 (helper)Dispatcher case in CallToolRequestHandler that routes 'search_notes' calls to the wrapper method.case 'search_notes': return await this.searchNotes(request.params.arguments);
- src/index.js:205-206 (helper)Wrapper method in TriliumMCPServer class that delegates to the actual searchNotes handler function.async searchNotes(args) { return await searchNotes(this.triliumClient, args);