fetch
Retrieve specific content from Calibre ebooks using epub:// URLs to access book sections directly from search results.
Instructions
Fetch specific content from a book using epub:// URL
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | epub:// URL from search results |
Implementation Reference
- server.js:359-407 (handler)Main handler function that executes the 'fetch' tool: parses epub:// URL, retrieves book metadata via calibredb, finds TXT file, extracts content by line range or default.async fetchByEpubUrl(url) { try { const { bookId, startLine, endLine } = this.parseEpubUrl(url); // Get book metadata const listResult = await this.runCalibreCommand([ 'list', '--fields', 'id,title,authors,formats', '--for-machine', '--search', `id:${bookId}` ]); const books = JSON.parse(listResult || '[]'); if (books.length === 0) { throw new Error('Book not found'); } const book = books[0]; const txtPath = book.formats?.find(f => f.endsWith('.txt')); if (!txtPath || !fs.existsSync(txtPath)) { throw new Error('No text format available for this book'); } // Extract content let content; if (startLine && endLine) { content = this.extractLineRange(txtPath, parseInt(startLine), parseInt(endLine)); } else { content = this.extractParagraphContext(txtPath, 1, 5); } return { book_id: book.id, title: book.title, authors: book.authors, content: content, url: url, line_range: { start: startLine ? parseInt(startLine) : null, end: endLine ? parseInt(endLine) : null } }; } catch (error) { throw error; } }
- server.js:547-560 (schema)Input schema definition for the 'fetch' tool in the tools/list response.{ name: 'fetch', description: 'Fetch specific content from a book using epub:// URL', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'epub:// URL from search results' } }, required: ['url'] } }
- server.js:595-622 (registration)Registration and dispatching logic for 'fetch' tool in handleToolsCall switch statement, calls the handler and formats MCP response.case 'fetch': const url = args.url; if (!url) { this.sendError(id, -32602, 'Missing required parameter: url'); return; } try { const fetchResult = await this.fetchByEpubUrl(url); const contentText = `Content from '${fetchResult.title}' by ${fetchResult.authors}:\n\n${fetchResult.content}`; const mcpResult = { content: [{ type: 'text', text: contentText }], book_id: fetchResult.book_id, title: fetchResult.title, authors: fetchResult.authors, url: fetchResult.url }; this.sendSuccess(id, mcpResult); } catch (error) { this.sendError(id, -32603, error.message); } break;
- server.js:152-173 (helper)Helper function to parse epub:// URL into bookId and optional line range, used by fetch handler.parseEpubUrl(url) { // Remove epub:// prefix url = url.replace(/^epub:\/\//, ''); // Extract book ID const idMatch = url.match(/@(\d+)/); if (!idMatch) { throw new Error('Invalid epub URL format'); } const bookId = idMatch[1]; // Extract line range (optional) let startLine = ''; let endLine = ''; const rangeMatch = url.match(/#(\d+):(\d+)$/); if (rangeMatch) { startLine = rangeMatch[1]; endLine = rangeMatch[2]; } return { bookId, startLine, endLine }; }