Skip to main content
Glama

read_multinote

Retrieve and read the complete content of multiple notes simultaneously using note IDs from the Joplin MCP Server for efficient data access.

Instructions

Read the full content of multiple notes at once

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
note_idsYesArray of note IDs to read

Implementation Reference

  • The ReadMultiNote class extending BaseTool. The `call` method implements the core tool logic: validates note IDs, fetches each note and its parent notebook from Joplin API, formats metadata (title, location, todo status, timestamps), note body in Markdown, handles not found/errors, and returns a structured summary.
    class ReadMultiNote extends BaseTool { async call(noteIds: string[]): Promise<string> { if (!noteIds || !Array.isArray(noteIds) || noteIds.length === 0) { return 'Please provide an array of note IDs. Example: read_multinote note_ids=["id1", "id2", "id3"]' } // Validate that all IDs look like valid note IDs const invalidIds = noteIds.filter((id) => !id || id.length < 10 || !id.match(/[a-f0-9]/i)) if (invalidIds.length > 0) { return `Error: Some IDs do not appear to be valid note IDs: ${invalidIds.join(", ")}\n\nNote IDs are long alphanumeric strings like "58a0a29f68bc4141b49c99f5d367638a".\n\nUse search_notes to find notes and their IDs.` } const resultLines: string[] = [] const notFound: string[] = [] const errors: string[] = [] const successful: string[] = [] // Add a header resultLines.push(`# Reading ${noteIds.length} notes\n`) // Process each note ID for (let i = 0; i < noteIds.length; i++) { const noteId = noteIds[i] resultLines.push(`## Note ${i + 1} of ${noteIds.length} (ID: ${noteId})\n`) try { // Get the note details with all relevant fields const note = await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, { query: { fields: "id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due", }, }) // Validate note response if (!note || typeof note !== "object" || !note.id) { errors.push(noteId) resultLines.push(`Error: Unexpected response format from Joplin API when fetching note ${noteId}\n`) continue } successful.push(noteId) // Get the notebook info to show where this note is located let notebookInfo = "Unknown notebook" if (note.parent_id) { try { const notebook = await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, { query: { fields: "id,title" }, }) if (notebook && notebook.title) { notebookInfo = `"${notebook.title}" (notebook_id: "${note.parent_id}")` } } catch (err: unknown) { process.stderr.write(`Error fetching notebook info for note ${noteId}: ${err}\n`) // Continue even if we can't get the notebook info } } // Add note metadata resultLines.push(`### Note: "${note.title}"`) resultLines.push(`Notebook: ${notebookInfo}`) // Add todo status if applicable if (note.is_todo) { const status = note.todo_completed ? "Completed" : "Not completed" resultLines.push(`Status: ${status}`) if (note.todo_due) { const dueDate = this.formatDate(note.todo_due) resultLines.push(`Due: ${dueDate}`) } } // Add timestamps const createdDate = this.formatDate(note.created_time) const updatedDate = this.formatDate(note.updated_time) resultLines.push(`Created: ${createdDate}`) resultLines.push(`Updated: ${updatedDate}`) // Add a separator before the note content resultLines.push("\n---\n") // Add the note body if (note.body) { resultLines.push(note.body) } else { resultLines.push("(This note has no content)") } // Add a separator after the note resultLines.push("\n---\n") } catch (error: any) { process.stderr.write(`Error reading note ${noteId}: ${error}\n`) if (error.response && error.response.status === 404) { notFound.push(noteId) resultLines.push(`Note with ID "${noteId}" not found.\n`) } else { errors.push(noteId) resultLines.push(`Error reading note: ${error.message || "Unknown error"}\n`) } } } // Add a summary at the end resultLines.push("# Summary") resultLines.push(`Total notes requested: ${noteIds.length}`) resultLines.push(`Successfully retrieved: ${successful.length}`) if (notFound.length > 0) { resultLines.push(`Notes not found: ${notFound.length}`) resultLines.push(`IDs not found: ${notFound.join(", ")}`) } if (errors.length > 0) { resultLines.push(`Errors encountered: ${errors.length}`) resultLines.push(`IDs with errors: ${errors.join(", ")}`) } return resultLines.join("\n") } } export default ReadMultiNote
  • Zod input schema for read_multinote tool: requires `note_ids` as array of strings.
    parameters: z.object({ note_ids: z.array(z.string()).describe("Array of note IDs to read"), }),
  • Registration of the read_multinote tool in the FastMCP server using server.addTool, including name, description, schema, and execute handler delegating to manager.
    server.addTool({ name: "read_multinote", description: "Read the full content of multiple notes at once", parameters: z.object({ note_ids: z.array(z.string()).describe("Array of note IDs to read"), }), execute: async (args) => { return await manager.readMultiNote(args.note_ids) }, })
  • JSON input schema for read_multinote tool in stdio MCP server listTools response.
    inputSchema: { type: "object", properties: { note_ids: { type: "array", items: { type: "string" }, description: "Array of note IDs to read" }, }, required: ["note_ids"], },
  • src/index.ts:245-248 (registration)
    Tool dispatch/registration in stdio server CallToolRequest handler switch statement, delegating to manager.readMultiNote.
    case "read_multinote": { const multiResult = await manager.readMultiNote(args.note_ids as string[]) return { content: [{ type: "text", text: multiResult }], isError: false } }

Other Tools

Related Tools

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