Skip to main content
Glama

Readwise MCP Server

by IAmAlexander
server.test.ts6.5 kB
import { ReadwiseMCPServer } from '../src/server.js'; import { Logger } from '../src/utils/logger.js'; import { ReadwiseClient } from '../src/api/client.js'; import { ReadwiseAPI } from '../src/api/readwise-api.js'; import { GetBooksTool } from '../src/tools/get-books.js'; import { GetHighlightsTool } from '../src/tools/get-highlights.js'; import { GetDocumentsTool } from '../src/tools/get-documents.js'; import { SearchHighlightsTool } from '../src/tools/search-highlights.js'; import { ReadwiseHighlightPrompt } from '../src/prompts/highlight-prompt.js'; import { ReadwiseSearchPrompt } from '../src/prompts/search-prompt.js'; import { MCPRequest, MCPResponse, ErrorResponse } from '../src/types/index.js'; // Mock the required dependencies jest.mock('../src/api/client'); jest.mock('../src/api/readwise-api'); jest.mock('../src/tools/get-books'); jest.mock('../src/tools/get-highlights'); jest.mock('../src/tools/get-documents'); jest.mock('../src/tools/search-highlights'); jest.mock('../src/prompts/highlight-prompt'); jest.mock('../src/prompts/search-prompt'); describe('ReadwiseMCPServer', () => { let server: ReadwiseMCPServer; let mockLogger: jest.Mocked<Logger>; beforeEach(() => { // Mock logger mockLogger = { debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn() } as unknown as jest.Mocked<Logger>; // Reset all mocks jest.clearAllMocks(); // Create the server server = new ReadwiseMCPServer('test-api-key', 3000, mockLogger, 'stdio'); }); it('should register all tools and prompts', () => { // Check if the tools and prompts were registered expect(GetBooksTool).toHaveBeenCalled(); expect(GetHighlightsTool).toHaveBeenCalled(); expect(GetDocumentsTool).toHaveBeenCalled(); expect(SearchHighlightsTool).toHaveBeenCalled(); expect(ReadwiseHighlightPrompt).toHaveBeenCalled(); expect(ReadwiseSearchPrompt).toHaveBeenCalled(); }); it('should handle tool calls correctly', async () => { // Create a mock tool call const toolCall: MCPRequest & { type: 'tool_call' } = { type: 'tool_call', name: 'get_books', parameters: { page_size: 10 }, request_id: 'test-request-id' }; // Create a mock callback const callback = jest.fn(); // Create a mock validate method for GetBooksTool const mockValidate = jest.fn().mockReturnValue({ success: true }); const mockExecute = jest.fn().mockResolvedValue({ results: [] }); // Set up the mock tool const mockTool = { name: 'get_books', validate: mockValidate, execute: mockExecute }; // Mock the private method to get the tool Object.defineProperty(server, 'toolRegistry', { get: jest.fn().mockReturnValue({ get: jest.fn().mockReturnValue(mockTool) }) }); // Call the handleMCPRequest method await server.handleMCPRequest(toolCall, callback); // Expect the validate and execute methods to be called expect(mockValidate).toHaveBeenCalledWith(toolCall.parameters); expect(mockExecute).toHaveBeenCalledWith(toolCall.parameters); // Expect the callback to be called with the result expect(callback).toHaveBeenCalledWith({ result: { results: [] } }); }); it('should handle validation errors in tool calls', async () => { // Create a mock tool call const toolCall: MCPRequest & { type: 'tool_call' } = { type: 'tool_call', name: 'get_books', parameters: { page: -1 }, // Invalid page request_id: 'test-request-id' }; // Create a mock callback const callback = jest.fn(); // Create a mock validate method that returns a validation error const mockValidate = jest.fn().mockReturnValue({ success: false, errors: [{ field: 'page', message: 'Page must be at least 1' }] }); // Set up the mock tool const mockTool = { name: 'get_books', validate: mockValidate, execute: jest.fn() }; // Mock the private method to get the tool Object.defineProperty(server, 'toolRegistry', { get: jest.fn().mockReturnValue({ get: jest.fn().mockReturnValue(mockTool) }) }); // Call the handleMCPRequest method await server.handleMCPRequest(toolCall, callback); // Expect the validate method to be called expect(mockValidate).toHaveBeenCalledWith(toolCall.parameters); // Expect the execute method not to be called expect(mockTool.execute).not.toHaveBeenCalled(); // Expect the callback to be called with an error expect(callback).toHaveBeenCalledWith(expect.objectContaining({ error: expect.objectContaining({ type: 'validation' }) })); }); it('should handle non-existent tools correctly', async () => { // Create a mock tool call with a non-existent tool const toolCall: MCPRequest & { type: 'tool_call' } = { type: 'tool_call', name: 'non_existent_tool', parameters: {}, request_id: 'test-request-id' }; // Create a mock callback const callback = jest.fn(); // Mock the toolRegistry.get method to return undefined Object.defineProperty(server, 'toolRegistry', { get: jest.fn().mockReturnValue({ get: jest.fn().mockReturnValue(undefined) }) }); // Call the handleMCPRequest method await server.handleMCPRequest(toolCall, callback); // Expect the callback to be called with an error expect(callback).toHaveBeenCalledWith(expect.objectContaining({ error: expect.objectContaining({ type: 'transport', details: expect.objectContaining({ code: 'tool_not_found' }) }) })); }); it('should handle invalid request types correctly', async () => { // Create a mock request with an invalid type const invalidRequest = { type: 'invalid_type', name: 'get_books', parameters: {}, request_id: 'test-request-id' }; // Create a mock callback const callback = jest.fn(); // Call the handleMCPRequest method await server.handleMCPRequest(invalidRequest as any, callback); // Expect the callback to be called with an error expect(callback).toHaveBeenCalledWith(expect.objectContaining({ error: expect.objectContaining({ type: 'transport', details: expect.objectContaining({ code: 'invalid_request_type' }) }) })); }); });

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