joplin-mcp
This server lets an AI read, search, and manage Joplin notes/notebooks through two script-execution tools.
Read-only tool (
execute_joplin_readonly_script):List, get, and view notebooks.
List, get, search, and read notes.
Inspect note sections, line ranges, and search within a note.
Get notebook trees and scoped trees.
Only read/search operations; no writes or deletions.
Full-access tool (
execute_joplin_script):Everything the read-only tool does.
Create, update, append, prepend, edit, delete, and move notes.
Supports todo fields when creating/updating notes.
Requires a notebook ID for creating notes.
Search capabilities:
Use rich syntax (
any:1,tag:,notebook:,title:,updated:,type:,iscompleted:, wildcards,-excluded).
Typical use cases:
Find notes about a topic.
Summarize or extract sections from large notes.
Create, organize, edit, or delete notes.
Move notes between notebooks.
Generate structured notebook overviews.
Allows AI agents to manage notes and notebooks in Joplin, including reading, searching, creating, updating, deleting, and organizing notes, with optional read-only mode and notebook scoping.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@joplin-mcpFind my notes about installing Arch Linux"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
joplin-mcp
Model Context Protocol (MCP) server for the Joplin note-taking app.
Designed by belsar.ai to be easy to install & enjoyable to use.
Video
https://www.youtube.com/watch?v=B3qJa7ycqNM&t=6s
Related MCP server: Joplin MCP Server
Platform Support
Available on macOS and Linux. Windows users should use WSL2.
Architecture
By default, this MCP server exposes two tools:
execute_joplin_readonly_script: For running scripts in read-only mode (inspecting/reading notes and notebooks).execute_joplin_script: For running scripts that can also perform modifications, creations, or deletions.
This script-based execution pattern follows Anthropic's recommended pattern for MCP servers and is the most performant, token-efficient way to build an MCP server today.
Scripts execute in a separate runner process isolated at the OS level by Anthropic Sandbox Runtime. node:vm limits the globals available inside that process; the Sandbox Runtime is the security boundary that blocks network access and filesystem writes.
joplin api ←http→ broker (allowlisted proxy) ←stdio→ runner (sandboxed)The runner calls joplin.* methods as if talking to Joplin directly, but all requests pass through the broker, which only permits a specific set of API methods. The broker is the only piece that can interact with your notes — and nothing else.
Quick Start
Open Joplin & navigate to tools > web clipper > enable web clipper service
The Joplin app needs to remain running (minimized is fine)
Add the server to your MCP client:
Claude Code:
claude mcp add --scope user --transport stdio joplin -- npx -y @belsar-ai/joplin-mcpCodex:
codex mcp add joplin -- npx -y @belsar-ai/joplin-mcpAgy: add this entry to ~/.gemini/config/mcp_config.json (or configure the same stdio server through /mcp):
{
"mcpServers": {
"joplin": {
"command": "npx",
"args": ["-y", "@belsar-ai/joplin-mcp"]
}
}
}That's it. Send a test request like "Find my notes about installing Fedora linux".
Linux Requirements
Install the following system packages before first use:
# Fedora
sudo dnf install bubblewrap socat ripgrep
# Ubuntu/Debian
sudo apt install bubblewrap socat ripgrepThe server will fail with an error message if these are missing.
Uninstall
claude mcp remove joplincodex mcp remove joplinagy mcp remove joplinConfiguration (Optional)
Notebook Scope Config
Create .mcp-config/joplin-mcp.toml in your project root to scope which notebooks are visible:
[defaults]
notebook = "Notes"
[scope]
notebooks = ["Notes", "Software"]defaults.notebook: Where new notes go if you don't specify a notebookscope.notebooks: Only these notebooks are visible to the AI
The config file is discovered by walking up from the current directory (like .git).
Read-Only Lock Mode
You can lock the MCP server down to a strict read-only mode so that no notes or notebooks can be modified, created, or deleted. In this mode, only the execute_joplin_readonly_script tool is exposed to the AI, and any attempts to execute write operations are blocked at the process level.
Configure read-only mode in one of two ways:
CLI Flag: Append
--readonlyto your start command:npx -y @belsar-ai/joplin-mcp --readonlyEnvironment Variable: Set
JOPLIN_READONLY=true(any value other thanfalse,0,no,off, or empty string will enable lock mode).
Example Usage
The AI can now handle complex requests in a single shot:
Find my notes about installing Arch Linux.Look for all notes whose last update time was before 2025 and move them to my Archive notebook.Show me all notes in my Work Projects notebook.Make a new note with a Mermaid diagram showing how a bill is passed on Capitol Hill.Working with Large Notes
The AI can navigate, search, and edit large notes without pulling the entire body into context:
Show me the table of contents for my platform docs note.Show me section 10.1 of my platform docs note.Search my project plan for "deadline".Replace "Q3 2025" with "Q4 2025" in my roadmap note.Available API (Script Context)
The AI has access to a global joplin object with the following methods:
Notes (joplin.notes)
readNote(id): Pretty-printed note with metadata header and body. Preferred for display.getNote(id): Raw note object. PreferreadNote()for display.searchNotes(query: string): Smart search with "any:1" logic.listAllNotes(fields?, ...): Get all notes.createNote(title, body, notebookId, ...): Create a new note.updateNote(id, updates): Update properties or body.appendToNote(id, text): Add text to the end.prependToNote(id, text): Add text to the beginning.editNote(id, oldString, newString, replaceAll?): Server-side string replacement. Fails if not found or ambiguous withoutreplaceAll.getNoteLineRange(id, startLine, endLine): Read a slice of a note by line number (1-indexed).searchInNote(id, pattern): Case-insensitive search within a note. Returns matches with line numbers and context.getNoteSections(id): Parse markdown headings into a table of contents with line numbers.deleteNote(id): Move to trash.moveNoteToNotebook(id, notebookId): Move a note to a different notebook.
Notebooks (joplin.notebooks) — read-only
listNotebooks(): Get folder structure.getNotebook(id): Get a single notebook.getNotebookNotes(notebookId, fields?, ...): Get notes in a notebook.getNotebookTree(notebookId, depth?): Get formatted tree of a notebook with notes.getAllNotebooksTree({ exclude? }): Get formatted tree of all notebooks.getScopedTree({ exclude?, depth? }): Get formatted tree of scoped notebooks with notes.
Troubleshooting
Verify Joplin desktop app is running
Confirm Web Clipper is enabled in Joplin settings
Ensure Joplin is listening on port 41184 (default)
Go outside for a nice walk
Available Tools
2 toolsexecute_joplin_readonly_scriptA
Execute JS with global 'joplin' object in read-only mode. Top-level await. Return the result. Modifications, deletions, and creations are blocked.
NOTEBOOKS (read-only): listNotebooks(fields?, orderBy?, orderDir?, limit?) getNotebook(id) getNotebookNotes(notebookId, fields?, orderBy?, orderDir?, limit?) getNotebookTree(notebookId, depth?) — formatted tree getAllNotebooksTree({ exclude? }) — notebooks only, respects scope getScopedTree({ exclude?, depth? }) — notebooks + notes, respects scope
NOTES (read-only): listAllNotes(fields?, includeDeleted?, orderBy?, orderDir?, limit?) searchNotes(query) — returns note array readNote(id) — formatted display with metadata + line numbers getNote(id) — raw object getNoteLineRange(id, startLine, endLine) searchInNote(id, pattern) getNoteSections(id)
Call as joplin.notebooks.X() or joplin.notes.X().
SEARCH: searchNotes("any:1 term1 term2"). Use OR/synonyms, not user's literal phrase. Syntax: "any:1", "tag:X", "notebook:X", "title:X", "updated:month-1", "type:todo", "iscompleted:0", "docker*", "-excluded".
RULES:
These methods return pre-formatted output: readNote, getNoteLineRange, searchInNote, getNoteSections, getNotebookTree, getAllNotebooksTree, getScopedTree. After calling them, respond only "Done." — do not repeat, summarize, or reformat the output.
| Name | Required | Description | Default |
|---|---|---|---|
| script | Yes | The JavaScript code to execute. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses all behavioral traits: read-only mode, blocking modifications, top-level await, return of results, and special handling of pre-formatted output methods. No annotations are provided, so the description fully carries the burden, and it does so thoroughly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is lengthy but well-organized into sections (notebooks, notes, search, rules). It is front-loaded with the core purpose. While every sentence is useful, the overall length could be slightly reduced without losing clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, no output schema, and single parameter, the description is exceptionally complete. It covers all available methods, search syntax, and behavioral rules, leaving no ambiguity about how to use the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the schema covers the single parameter 'script' with a basic description, the tool description adds significant context by specifying the expected code structure (e.g., using joplin methods, search syntax) and providing detailed API references.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool executes JavaScript with the 'joplin' object in read-only mode. It lists available methods for notebooks and notes, and explicitly distinguishes from the sibling by emphasizing read-only operations.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides comprehensive usage guidelines, including how to call methods (e.g., joplin.notebooks.X()), search syntax with examples, and rules for pre-formatted output (respond only 'Done'). It implies when to use this tool versus the sibling by specifying read-only mode.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
execute_joplin_scriptA
Execute JS with global 'joplin' object with write/destructive permissions. Top-level await. Return the result. Supports all read-only methods, search syntax, and call patterns defined in 'execute_joplin_readonly_script', plus the following modifying/destructive methods:
NOTES (write/destructive): createNote(title, body, notebookId?, tags?, isTodo?, todoDue?, todoCompleted?) updateNote(id, { title?, body?, parent_id?, is_todo?, todo_due?, todo_completed? }) appendToNote(id, content) prependToNote(id, content) deleteNote(id) moveNoteToNotebook(noteId, notebookId) editNote(id, oldString, newString, replaceAll?)
RULES:
createNote needs notebookId — call listNotebooks() first.
editNote returns pre-formatted output: after calling it, respond only "Done." — do not repeat, summarize, or reformat the output.
| Name | Required | Description | Default |
|---|---|---|---|
| script | Yes | The JavaScript code to execute. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite no annotations, the description fully discloses the write/destructive nature, lists all destructive methods, mentions top-level await, and provides specific output behavior for editNote. Minor gaps include not addressing error handling or execution limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: start with purpose, then methods, then rules. However, it is somewhat lengthy; each sentence adds value, but could be tightened slightly for better conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one parameter and no output schema or annotations, the description is fairly complete. It covers methods, rules, and return behavior. Missing details include potential execution limits or error handling, but overall adequate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema coverage, the baseline is 3, but the description adds substantial meaning by detailing the expected global 'joplin' object, available methods, and rules, far exceeding what the schema's simple string description provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description precisely states it executes JavaScript with write/destructive permissions using a global 'joplin' object. It distinguishes itself from the sibling readonly tool by explicitly listing the additional destructive methods, making its purpose clear and unique.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance, including the rule that createNote requires calling listNotebooks() first, and a special output handling rule for editNote. It also contrasts with the readonly sibling, offering clear alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v4.1.1- First observed
execute_joplin_readonly_script - First observed
execute_joplin_script
TDQS
Scored across 2 tools
The two tools have nearly identical names and both execute JavaScript, differing only in permissions (read-only vs. write). The descriptions are detailed enough to distinguish them, but the overlap in core functionality could cause initial confusion for an agent.
Both tool names follow a consistent 'execute_joplin_<permission>_script' pattern, using snake_case and clear permission labels (readonly vs. script). No mixing of conventions.
Only 2 tools for a server that exposes a full API (CRUD for notebooks, notes, search, etc.) feels minimal. While the design intentionally wraps everything into two script executors, a more granular tool surface might be expected given the scope.
All operations available through the Joplin API are accessible via the two tools (read-only in one, write in the other). No obvious gaps in functionality; the set fully covers the domain.
Maintenance
Related MCP Connectors
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Markdown-based note-taking with a hosted MCP server. Your notes serve you and your AI.
Google Keep-style notes app with an MCP server for AI agents to read/write notes.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseAqualityDmaintenanceA server that enables AI assistants to access and manipulate Joplin notes through the Model Context Protocol, allowing search, reading, editing, and management of notes using natural language.690MIT
- AlicenseNot gradedqualityDmaintenanceMCP server that provides standardized tools for querying and retrieving notes from Joplin personal knowledge manager through its API, enabling AI assistants to access and reference personal notes contextually.9MIT
- AlicenseNot gradedqualityAmaintenanceMCP server for Joplin Server that gives LLMs full access to notes, notebooks, tags, and attachments via the REST API.5MIT
- AlicenseAqualityAmaintenanceA minimal MCP server that enables interaction with Joplin notes and notebooks through the local Web Clipper REST API, providing tools for search, create, update, and note management.13MIT