Skip to main content
Glama

Readwise MCP Server

by IAmAlexander
advanced-search.ts5.85 kB
import { BaseMCPTool } from '../mcp/registry/base-tool.js'; import { ReadwiseAPI } from '../api/readwise-api.js'; import { AdvancedSearchParams, AdvancedSearchResult, MCPToolResult, isAPIError } from '../types/index.js'; import { ValidationResult, validateArray, validateNumberRange, validateAllowedValues } from '../types/validation.js'; import type { Logger } from '../utils/logger-interface.js'; /** * Tool for advanced search with multiple filters and facets */ export class AdvancedSearchTool extends BaseMCPTool<AdvancedSearchParams, AdvancedSearchResult> { /** * The name of the tool */ readonly name = 'advanced_search'; /** * The description of the tool */ readonly description = 'Search highlights with advanced filters and facets'; /** * The JSON Schema parameters for the tool */ readonly parameters = { type: 'object', properties: { query: { type: 'string', description: 'Optional search query' }, book_ids: { type: 'array', items: { type: 'string' }, description: 'Optional list of book IDs to filter by' }, tags: { type: 'array', items: { type: 'string' }, description: 'Optional list of tags to filter by' }, categories: { type: 'array', items: { type: 'string' }, description: 'Optional list of categories to filter by' }, date_range: { type: 'object', properties: { start: { type: 'string', description: 'Start date in ISO format' }, end: { type: 'string', description: 'End date in ISO format' } } }, location_range: { type: 'object', properties: { start: { type: 'number', description: 'Start location' }, end: { type: 'number', description: 'End location' } } }, has_note: { type: 'boolean', description: 'Filter highlights that have notes' }, sort_by: { type: 'string', enum: ['created_at', 'updated_at', 'highlighted_at', 'location'], description: 'Field to sort by' }, sort_order: { type: 'string', enum: ['asc', 'desc'], description: 'Sort order' }, page: { type: 'number', description: 'Page number for pagination' }, page_size: { type: 'number', description: 'Number of results per page' } } }; /** * Create a new AdvancedSearchTool * @param api - The ReadwiseAPI instance to use * @param logger - The logger instance */ constructor(private api: ReadwiseAPI, logger: Logger) { super(logger); } /** * Validate the parameters * @param params - The parameters to validate * @returns Validation result */ validate(params: AdvancedSearchParams): ValidationResult { const validations = []; // Validate arrays if provided if (params.book_ids !== undefined) { validations.push(validateArray(params, 'book_ids', 'Book IDs must be an array of strings')); } if (params.tags !== undefined) { validations.push(validateArray(params, 'tags', 'Tags must be an array of strings')); } if (params.categories !== undefined) { validations.push(validateArray(params, 'categories', 'Categories must be an array of strings')); } // Validate sort parameters if provided if (params.sort_by !== undefined) { validations.push( validateAllowedValues( params, 'sort_by', ['created_at', 'updated_at', 'highlighted_at', 'location'], 'Invalid sort field' ) ); } if (params.sort_order !== undefined) { validations.push( validateAllowedValues( params, 'sort_order', ['asc', 'desc'], 'Sort order must be "asc" or "desc"' ) ); } // Validate pagination parameters if provided if (params.page !== undefined) { validations.push( validateNumberRange(params, 'page', 1, undefined, 'Page must be a positive number') ); } if (params.page_size !== undefined) { validations.push( validateNumberRange(params, 'page_size', 1, 100, 'Page size must be between 1 and 100') ); } // Check each validation result for (const validation of validations) { if (!validation.valid) { return validation; } } // All validations passed return super.validate(params); } /** * Execute the tool * @param params - The parameters for the request * @returns Promise resolving to an object with a result property containing the search results */ async execute(params: AdvancedSearchParams): Promise<MCPToolResult<AdvancedSearchResult>> { try { this.logger.debug('Executing advanced_search tool', params as any); const result = await this.api.advancedSearch(params); this.logger.debug(`Found ${result.highlights.count} highlights`); return { result }; } catch (error) { this.logger.error('Error executing advanced_search tool', error as any); // Re-throw API errors if (isAPIError(error)) { throw error; } // Handle unexpected errors with proper result format return { result: { highlights: { count: 0, next: null, previous: null, results: [] } }, success: false, error: 'An unexpected error occurred while searching' }; } } }

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/IAmAlexander/readwise-mcp'

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