Skip to main content
Glama
jordanburke

joplin-mcp-server

create_note

Generate and organize notes in Joplin by adding titles, markdown or HTML content, todos, and images, while optionally nesting them within specific notebooks.

Instructions

Create a new note in Joplin

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
bodyNoNote content in Markdown
body_htmlNoNote content in HTML
image_data_urlNoBase64 encoded image data URL
is_todoNoWhether this is a todo note
parent_idNoID of parent notebook
titleNoNote title

Implementation Reference

  • Core implementation of the create_note tool: CreateNote class extending BaseTool with the async call(options) method that handles note creation via Joplin API, validation, error handling, and formatted response.
    class CreateNote extends BaseTool {
      async call(options: CreateNoteOptions): Promise<string> {
        if (!options || typeof options !== "object") {
          return 'Please provide note creation options. Example: create_note {"title": "My Note", "body": "Note content"}'
        }
    
        // Validate that we have at least a title or body
        if (!options.title && !options.body && !options.body_html) {
          return "Please provide at least a title, body, or body_html for the note."
        }
    
        // Validate parent_id if provided
        if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {
          return `Error: "${options.parent_id}" does not appear to be a valid notebook ID.\n\nNotebook IDs are long alphanumeric strings like "58a0a29f68bc4141b49c99f5d367638a".\n\nUse list_notebooks to see available notebooks and their IDs.`
        }
    
        try {
          // Prepare the request body
          const requestBody: CreateNoteOptions = {}
    
          if (options.title) requestBody.title = options.title
          if (options.body) requestBody.body = options.body
          if (options.body_html) requestBody.body_html = options.body_html
          if (options.parent_id) requestBody.parent_id = options.parent_id
          if (options.is_todo !== undefined) requestBody.is_todo = options.is_todo
          if (options.image_data_url) requestBody.image_data_url = options.image_data_url
    
          // Create the note
          const createdNote = await this.apiClient.post<CreateNoteResponse>("/notes", requestBody)
    
          // Validate response
          if (!createdNote || typeof createdNote !== "object" || !createdNote.id) {
            return "Error: Unexpected response format from Joplin API when creating note"
          }
    
          // Get notebook info if available
          let notebookInfo = "Root level"
          if (createdNote.parent_id) {
            try {
              const notebook = await this.apiClient.get(`/folders/${createdNote.parent_id}`, {
                query: { fields: "id,title" },
              })
              if (notebook && notebook.title) {
                notebookInfo = `"${notebook.title}" (notebook_id: "${createdNote.parent_id}")`
              }
            } catch {
              // Continue even if we can't get notebook info
              notebookInfo = `Notebook ID: ${createdNote.parent_id}`
            }
          }
    
          // Format success response
          const resultLines: string[] = []
          resultLines.push(`βœ… Successfully created note!`)
          resultLines.push("")
          resultLines.push(`πŸ“ Note Details:`)
          resultLines.push(`   Title: "${createdNote.title || "Untitled"}"`)
          resultLines.push(`   Note ID: ${createdNote.id}`)
          resultLines.push(`   Location: ${notebookInfo}`)
    
          if (createdNote.is_todo) {
            resultLines.push(`   Type: Todo item`)
          }
    
          const createdDate = this.formatDate(createdNote.created_time)
          resultLines.push(`   Created: ${createdDate}`)
    
          resultLines.push("")
          resultLines.push(`πŸ”— Next steps:`)
          resultLines.push(`   - Read the note: read_note note_id="${createdNote.id}"`)
          if (createdNote.parent_id) {
            resultLines.push(`   - View notebook: read_notebook notebook_id="${createdNote.parent_id}"`)
          }
          resultLines.push(`   - Search for it: search_notes query="${createdNote.title}"`)
    
          return resultLines.join("\n")
        } catch (error: any) {
          if (error.response) {
            // Handle specific API errors
            if (error.response.status === 400) {
              return `Error creating note: Invalid request data.\n\nPlease check your input parameters. ${error.response.data?.error || ""}`
            }
            if (error.response.status === 404 && options.parent_id) {
              return `Error: Notebook with ID "${options.parent_id}" not found.\n\nUse list_notebooks to see available notebooks and their IDs.`
            }
          }
          return this.formatError(error, "creating note")
        }
      }
    }
  • TypeScript interface defining the input parameters for the create_note tool.
    interface CreateNoteOptions {
      title?: string | undefined
      body?: string | undefined
      body_html?: string | undefined
      parent_id?: string | undefined
      is_todo?: boolean | undefined
      image_data_url?: string | undefined
    }
  • Registration of create_note tool in FastMCP server, including Zod input schema and execution delegating to JoplinServerManager.createNote.
    server.addTool({
      name: "create_note",
      description: "Create a new note in Joplin",
      parameters: z.object({
        title: z.string().optional().describe("Note title"),
        body: z.string().optional().describe("Note content in Markdown"),
        body_html: z.string().optional().describe("Note content in HTML"),
        parent_id: z.string().optional().describe("ID of parent notebook"),
        is_todo: z.boolean().optional().describe("Whether this is a todo note"),
        image_data_url: z.string().optional().describe("Base64 encoded image data URL"),
      }),
      execute: async (args) => {
        return await manager.createNote(args)
      },
    })
  • src/index.ts:132-145 (registration)
    Tool schema registration for create_note in the stdio MCP server's ListTools response.
      name: "create_note",
      description: "Create a new note in Joplin",
      inputSchema: {
        type: "object",
        properties: {
          title: { type: "string", description: "Note title" },
          body: { type: "string", description: "Note content in Markdown" },
          body_html: { type: "string", description: "Note content in HTML" },
          parent_id: { type: "string", description: "ID of parent notebook" },
          is_todo: { type: "boolean", description: "Whether this is a todo note" },
          image_data_url: { type: "string", description: "Base64 encoded image data URL" },
        },
      },
    },
  • JoplinServerManager wrapper method for createNote that delegates to the instantiated CreateNote tool instance.
    async createNote(params: {
      title?: string | undefined
      body?: string | undefined
      body_html?: string | undefined
      parent_id?: string | undefined
      is_todo?: boolean | undefined
      image_data_url?: string | undefined
    }): Promise<string> {
      return await this.tools.createNote.call(params)
    }
Install Server

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