Skip to main content
Glama
edricgsh

Readwise Reader MCP Server

by edricgsh

readwise_list_documents

Retrieve documents from Readwise Reader with filtering options for location, category, tags, and date ranges to organize your saved content.

Instructions

List documents from Readwise Reader with optional filtering

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
idNoFilter by specific document ID
updatedAfterNoFilter documents updated after this date (ISO 8601)
addedAfterNoFilter documents added after this date (ISO 8601). Note: This will fetch all documents first and then filter client-side.
locationNoFilter by document location
categoryNoFilter by document category
tagNoFilter by tag name
pageCursorNoPage cursor for pagination
withHtmlContentNo⚠️ PERFORMANCE WARNING: Include HTML content in the response. This significantly slows down the API. Only use when explicitly requested by the user or when raw HTML is specifically needed for the task.
withFullContentNo⚠️ PERFORMANCE WARNING: Include full converted text content in the response. This significantly slows down the API as it fetches and processes each document's content. Only use when explicitly requested by the user or when document content is specifically needed for analysis/reading. Default: false for performance.

Implementation Reference

  • The handleListDocuments function implements the core logic for the readwise_list_documents tool. It initializes the Readwise client, handles filtering (including client-side for addedAfter), pagination, fetches documents, optionally converts content to text or includes HTML, and formats the response.
    export async function handleListDocuments(args: any) {
      const client = initializeClient();
      const params = args as ListDocumentsParams;
      
      // If withFullContent is true, we also need HTML content
      if (params.withFullContent === true) {
        params.withHtmlContent = true;
      }
      
      let response;
      let clientSideFiltered = false;
      
      // If addedAfter is specified, we need to fetch all documents and filter client-side
      if (params.addedAfter) {
        clientSideFiltered = true;
        const addedAfterDate = new Date(params.addedAfter);
        
        // Create params without addedAfter for the API call
        const apiParams = { ...params };
        delete apiParams.addedAfter;
        
        // Fetch all documents if no other pagination is specified
        if (!apiParams.pageCursor && !apiParams.limit) {
          const allDocuments: any[] = [];
          let nextPageCursor: string | undefined;
          
          do {
            const fetchParams = { ...apiParams };
            if (nextPageCursor) {
              fetchParams.pageCursor = nextPageCursor;
            }
            
            const pageResponse = await client.listDocuments(fetchParams);
            allDocuments.push(...pageResponse.data.results);
            nextPageCursor = pageResponse.data.nextPageCursor;
          } while (nextPageCursor);
          
          // Filter documents by addedAfter date
          const filteredDocuments = allDocuments.filter(doc => {
            if (!doc.saved_at) return false;
            const savedDate = new Date(doc.saved_at);
            return savedDate > addedAfterDate;
          });
          
          response = {
            data: {
              count: filteredDocuments.length,
              nextPageCursor: undefined,
              results: filteredDocuments
            },
            messages: []
          };
        } else {
          // If pagination is specified, just do a regular API call and filter the current page
          response = await client.listDocuments(apiParams);
          const filteredDocuments = response.data.results.filter(doc => {
            if (!doc.saved_at) return false;
            const savedDate = new Date(doc.saved_at);
            return savedDate > addedAfterDate;
          });
          
          response.data.results = filteredDocuments;
          response.data.count = filteredDocuments.length;
        }
      } else {
        response = await client.listDocuments(params);
      }
    
      // Convert content to LLM-friendly text for documents only if withFullContent is explicitly true
      const shouldIncludeContent = params.withFullContent === true; // Default to false for performance
      const documentsWithText = await Promise.all(
        response.data.results.map(async (doc) => {
          let content = '';
          if (shouldIncludeContent) {
            // Try to use HTML content first (from Readwise), fallback to URL fetching
            if (doc.html_content) {
              // Use HTML content from Readwise for non-jina content types
              const shouldUseJina = !doc.category || doc.category === 'article' || doc.category === 'pdf';
              if (shouldUseJina) {
                const urlToConvert = doc.source_url || doc.url;
                if (urlToConvert) {
                  content = await convertUrlToText(urlToConvert, doc.category);
                }
              } else {
                content = extractTextFromHtml(doc.html_content);
              }
            } else {
              // Fallback to URL fetching if no HTML content available
              const urlToConvert = doc.source_url || doc.url;
              if (urlToConvert) {
                content = await convertUrlToText(urlToConvert, doc.category);
              }
            }
          }
          
          const result: 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,
            parent_id: doc.parent_id,
            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,
          };
          
          if (shouldIncludeContent) {
            result.content = content; // LLM-friendly text content instead of raw HTML
          }
          
          if (params.withHtmlContent && doc.html_content) {
            result.html_content = doc.html_content;
          }
          
          return result;
        })
      );
    
      let responseText = JSON.stringify({
        count: response.data.count,
        nextPageCursor: response.data.nextPageCursor,
        documents: documentsWithText
      }, null, 2);
      
      let allMessages = response.messages || [];
      
      // Add message about client-side filtering if it was performed
      if (clientSideFiltered) {
        allMessages.push({
          type: 'info',
          content: 'Documents were filtered client-side based on the addedAfter date. All documents were fetched from the API first, then filtered by their saved_at date.'
        });
      }
      
      if (allMessages.length > 0) {
        responseText += '\n\nMessages:\n' + allMessages.map(msg => `${msg.type.toUpperCase()}: ${msg.content}`).join('\n');
      }
    
      return {
        content: [
          {
            type: 'text',
            text: responseText,
          },
        ],
      };
    }
  • Defines the tool metadata including name, description, and detailed inputSchema with parameters like addedAfter, withFullContent, etc., for the readwise_list_documents tool.
    {
      name: 'readwise_list_documents',
      description: 'List documents from Readwise Reader with optional filtering',
      inputSchema: {
        type: 'object',
        properties: {
          id: {
            type: 'string',
            description: 'Filter by specific document ID',
          },
          updatedAfter: {
            type: 'string',
            description: 'Filter documents updated after this date (ISO 8601)',
          },
          addedAfter: {
            type: 'string',
            description: 'Filter documents added after this date (ISO 8601). Note: This will fetch all documents first and then filter client-side.',
          },
          location: {
            type: 'string',
            enum: ['new', 'later', 'shortlist', 'archive', 'feed'],
            description: 'Filter by document location',
          },
          category: {
            type: 'string',
            enum: ['article', 'book', 'tweet', 'pdf', 'email', 'youtube', 'podcast'],
            description: 'Filter by document category',
          },
          tag: {
            type: 'string',
            description: 'Filter by tag name',
          },
          pageCursor: {
            type: 'string',
            description: 'Page cursor for pagination',
          },
          withHtmlContent: {
            type: 'boolean',
            description: '⚠️ PERFORMANCE WARNING: Include HTML content in the response. This significantly slows down the API. Only use when explicitly requested by the user or when raw HTML is specifically needed for the task.',
          },
          withFullContent: {
            type: 'boolean',
            description: '⚠️ PERFORMANCE WARNING: Include full converted text content in the response. This significantly slows down the API as it fetches and processes each document\'s content. Only use when explicitly requested by the user or when document content is specifically needed for analysis/reading. Default: false for performance.',
          },
        },
        additionalProperties: false,
      },
    },
  • Registers the 'readwise_list_documents' tool name in the switch statement, dispatching to the handleListDocuments function.
    case 'readwise_list_documents':
      return handleListDocuments(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/edricgsh/Readwise-Reader-MCP'

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