Skip to main content
Glama
RadonX
by RadonX

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
NameRequiredDescriptionDefault
queryYesSearch query (fulltext or structured, max 500 characters)
limitNoMaximum number of results to return

Implementation Reference

  • 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'], }, },
  • 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; }
  • Dispatcher case in CallToolRequestHandler that routes 'search_notes' calls to the wrapper method.
    case 'search_notes': return await this.searchNotes(request.params.arguments);
  • Wrapper method in TriliumMCPServer class that delegates to the actual searchNotes handler function.
    async searchNotes(args) { return await searchNotes(this.triliumClient, args);

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/RadonX/mcp-trilium'

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