Skip to main content
Glama
read-notebook.ts3.56 kB
import BaseTool, { JoplinFolder, JoplinNote } from "./base-tool.js" interface NotebookNotesResponse { items: JoplinNote[] } class ReadNotebook extends BaseTool { async call(notebookId: string): Promise<string> { const validationError = this.validateId(notebookId, "notebook") if (validationError) { return validationError } try { // First, get the notebook details const notebook = await this.apiClient.get<JoplinFolder>(`/folders/${notebookId}`, { query: { fields: "id,title,parent_id" }, }) // Validate notebook response if (!notebook || typeof notebook !== "object" || !notebook.id) { return `Error: Unexpected response format from Joplin API when fetching notebook` } // Get all notes in this notebook const notes = await this.apiClient.get<NotebookNotesResponse>(`/folders/${notebookId}/notes`, { query: { fields: "id,title,updated_time,is_todo,todo_completed" }, }) // Validate notes response if (!notes || typeof notes !== "object") { return `Error: Unexpected response format from Joplin API when fetching notes` } if (!notes.items || !Array.isArray(notes.items) || notes.items.length === 0) { return `Notebook "${notebook.title}" (notebook_id: "${notebook.id}") is empty.\n\nTry another notebook ID or use list_notebooks to see all available notebooks.` } // Format the notebook contents const resultLines: string[] = [] resultLines.push(`# Notebook: "${notebook.title}" (notebook_id: "${notebook.id}")`) resultLines.push(`Contains ${notes.items.length} notes:\n`) resultLines.push(`NOTE: This is showing the contents of notebook "${notebook.title}", not a specific note.\n`) // If multiple notes were found, add a hint about read_multinote if (notes.items.length > 1) { const noteIds = notes.items.map((note) => note.id) resultLines.push(`TIP: To read all ${notes.items.length} notes at once, use:\n`) resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\n`) } // Sort notes by updated_time (newest first) const sortedNotes = [...notes.items].sort((a, b) => b.updated_time - a.updated_time) sortedNotes.forEach((note) => { const updatedDate = this.formatDate(note.updated_time) // Add checkbox for todos if (note.is_todo) { const checkboxStatus = note.todo_completed ? "✅" : "☐" resultLines.push(`- ${checkboxStatus} Note: "${note.title}" (note_id: "${note.id}")`) } else { resultLines.push(`- Note: "${note.title}" (note_id: "${note.id}")`) } resultLines.push(` Updated: ${updatedDate}`) resultLines.push(` To read this note: read_note note_id="${note.id}"`) resultLines.push("") // Empty line between notes }) return resultLines.join("\n") } catch (error: any) { if (error.response && error.response.status === 404) { return `Notebook with ID "${notebookId}" not found.\n\nThis might happen if:\n1. The ID is incorrect\n2. You're using a note title instead of a notebook ID\n3. The notebook has been deleted\n\nUse list_notebooks to see all available notebooks with their IDs.` } return ( this.formatError(error, "reading notebook") + `\n\nMake sure you're using a valid notebook ID, not a note title.\nUse list_notebooks to see all available notebooks with their IDs.` ) } } } export default ReadNotebook

Implementation Reference

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/jordanburke/joplin-mcp-server'

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