readwise_topic_search
Search documents in Readwise Reader by topic using regex matching on titles, summaries, notes, and tags to find relevant content.
Instructions
Search documents in Readwise Reader by topic using regex matching on title, summary, notes, and tags
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| searchTerms | Yes | List of search terms to match against document content (case-insensitive regex matching) |
Implementation Reference
- The main handler function that executes the readwise_topic_search tool. It extracts searchTerms from input args, calls the client's searchDocumentsByTopic method, formats the matching documents into a structured JSON response with full metadata, appends any API messages, and returns as MCP text content.export async function handleTopicSearch(args: any) { const client = initializeClient(); const { searchTerms } = args as { searchTerms: string[] }; const response = await client.searchDocumentsByTopic(searchTerms); const searchResults = { searchTerms, totalMatches: response.data.length, documents: response.data.map((doc: any) => ({ id: doc.id, url: doc.url, title: doc.title, author: doc.author, source: doc.source, category: doc.category, location: doc.location, tags: doc.tags, site_name: doc.site_name, word_count: doc.word_count, created_at: doc.created_at, updated_at: doc.updated_at, published_date: doc.published_date, summary: doc.summary, image_url: doc.image_url, source_url: doc.source_url, notes: doc.notes, reading_progress: doc.reading_progress, first_opened_at: doc.first_opened_at, last_opened_at: doc.last_opened_at, saved_at: doc.saved_at, last_moved_at: doc.last_moved_at, })) }; let responseText = JSON.stringify(searchResults, null, 2); if (response.messages && response.messages.length > 0) { responseText += '\n\nMessages:\n' + response.messages.map(msg => `${msg.type.toUpperCase()}: ${msg.content}`).join('\n'); } return { content: [ { type: 'text', text: responseText, }, ], }; }
- src/readwise-client.ts:254-305 (helper)Supporting utility in ReadwiseClient that implements the topic search logic: paginates to fetch all documents, builds case-insensitive regex from each searchTerm, concatenates title/summary/notes/tags for matching, filters documents matching any term, handles rate limiting.async searchDocumentsByTopic(searchTerms: string[]): Promise<APIResponse<ReadwiseDocument[]>> { try { // Fetch all documents without full content for performance const allDocuments: ReadwiseDocument[] = []; let nextPageCursor: string | undefined; do { const params: ListDocumentsParams = { withFullContent: false, withHtmlContent: false, }; if (nextPageCursor) { params.pageCursor = nextPageCursor; } const response = await this.listDocuments(params); allDocuments.push(...response.data.results); nextPageCursor = response.data.nextPageCursor; } while (nextPageCursor); // Create regex patterns from search terms (case-insensitive) const regexPatterns = searchTerms.map(term => new RegExp(term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i') ); // Filter documents that match any of the search terms const matchingDocuments = allDocuments.filter(doc => { // Extract searchable text fields const searchableFields = [ doc.title || '', doc.summary || '', doc.notes || '', // Handle tags - they can be string array or object Array.isArray(doc.tags) ? doc.tags.join(' ') : '', ]; const searchableText = searchableFields.join(' ').toLowerCase(); // Check if any regex pattern matches return regexPatterns.some(pattern => pattern.test(searchableText)); }); return this.createResponse(matchingDocuments); } catch (error) { if (error instanceof Error && error.message.startsWith('RATE_LIMIT:')) { const seconds = parseInt(error.message.split(':')[1], 10); throw new Error(`Rate limit exceeded. Too many requests. Please retry after ${seconds} seconds.`); } throw error; } }
- Tool schema definition including name, description, and inputSchema specifying an array of searchTerms (min 1 item) for regex-based topic search.{ name: 'readwise_topic_search', description: 'Search documents in Readwise Reader by topic using regex matching on title, summary, notes, and tags', inputSchema: { type: 'object', properties: { searchTerms: { type: 'array', items: { type: 'string' }, description: 'List of search terms to match against document content (case-insensitive regex matching)', minItems: 1, }, }, required: ['searchTerms'], additionalProperties: false, }, },
- src/handlers/index.ts:35-36 (registration)Tool dispatch registration in the central handleToolCall switch statement, mapping 'readwise_topic_search' name to the handleTopicSearch function.case 'readwise_topic_search': return handleTopicSearch(args);
- src/handlers/index.ts:7-7 (registration)Import statement registering the handleTopicSearch handler into the dispatch module.import { handleListTags, handleTopicSearch } from './tag-search-handlers.js';