FastMCP server exposing Joplin notes, notebooks, tags, and imports.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste into ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
A FastMCP-based Model Context Protocol (MCP) server for Joplin note-taking application via its Python API joppy, enabling AI assistants to interact with your Joplin notes, notebooks, and tags through a standardized interface.
This MCP server provides 26 optimized tools for comprehensive Joplin integration:
find_notes (supports trash=True for trashed notes), find_notes_with_tag, find_notes_in_notebook, get_all_notesget_note, get_note_resources (read OCR text from attached images/PDFs), get_links, create_note, update_note, edit_note, delete_notelist_notebooks, create_notebook, update_notebook, delete_notebooklist_tags, create_tag, update_tag, delete_tag, get_tags_by_notetag_note, untag_noterestore_from_trash - Restore soft-deleted notes or notebooksimport_from_file - Import Markdown, HTML, CSV, TXT, JEX files and directoriesping_joplinAny MCP-compatible client should work. Below are the ones with documented setup instructions.
Run the automated installer:
This script will:
After setup, restart Claude Desktop and you're ready to go!
Install the orchestration plugin for smarter tool usage (edit vs update, long-note reading, bulk tagging):
You'll be prompted for your Joplin API token on first use. The skill is invoked automatically when working with Joplin tools, or manually with /joplin.
Install Jan AI from https://jan.ai
Add MCP Server in Jan's interface:
joplinuvx --from joplin-mcp joplin-mcp-server (requires uv installed)JOPLIN_TOKEN: your_joplin_api_token_hereStart chatting with access to your Joplin notes!
Automated Setup (Alternative)
This will detect and configure Jan AI automatically, just like Claude Desktop.
Auto-discovery (if you set up Claude Desktop first)
Manual setup (works independently)
Once configured, you can ask your AI assistant:
find_notes(task=True)The setup script offers 4 permission levels:
Choose the level that matches your comfort and use case.
Restrict AI access to specific notebooks using pattern-based access control. When configured, only matching notebooks (and their contents) are visible β all other notebooks are hidden.
JSON config (joplin-mcp.json):
Environment variable:
Patterns use gitignore/gitwildmatch semantics:
| Pattern | Matches | Example |
|---|---|---|
Work | Exact notebook name (and all children) | Work, Work/Tasks, Work/Notes |
Projects/* | Direct children of Projects | Projects/Alpha, Projects/Beta |
Projects/** | All descendants recursively | Projects/Alpha/Tasks/Q1 |
!Projects/Secret | Exclude (negate) a specific path | Everything in Projects except Secret |
Negation patterns always win over positive patterns (any negation match on a path or ancestor denies access).
Projects means notes in Projects/Work/Tasks are also accessible.get_note, get_note_resources, find_notes, get_links β notes in blocked notebooks are filtered out or rejected.create_note, update_note, edit_note, delete_note β operations on notes in blocked notebooks are rejected.list_notebooks only shows accessible notebooks. create_notebook is rejected both under a blocked parent and at the top level (no parent_name) when an allowlist is set, and update_notebook rejects moves to top-level (parent_name="/") under the same policy β both would let the agent silently move a notebook out of allowlist-enforced scope. To grow the allowlist, create or relocate the notebook in the Joplin UI, then add it to notebook_allowlist and restart the server.find_notes results are filtered to only include notes in accessible notebooks.tag_note, untag_note, get_tags_by_note enforce access on the note's notebook.Single project focus (notebook name containing a space):
Multiple notebooks with exclusion:
Glob patterns:
No allowlist (default) β all notebooks accessible:
At server startup, the allowlist is validated and logged:
For developers or users who want the latest features:
bootstrap.py is cross-platform: it offers to create a ./venv, runs pip install -e ., then launches the interactive installer. Pass --no-venv to install into whichever Python is already active.
If you prefer manual setup or the script doesn't work:
Note on
uvx:uvxruns Python applications without permanently installing them (requiresuv:pip install uv). It can read and write user configuration files (e.g., Claude/Jan configs), souvx --from joplin-mcp joplin-mcp-installworks for setup just like a pip install.
Version pinning (optional): For longβlived client configs or CI, you can pin or range-constrain the version for reproducibility, e.g.
uvx --from joplin-mcp==0.4.1 joplin-mcp-installoruvx --from 'joplin-mcp>=0.4,<0.5' joplin-mcp-install.
Create joplin-mcp.json in your project directory:
Add to your claude_desktop_config.json:
Option A: Using uvx (Zero-install)
Requires uv installed: pip install uv
Option B: Using installed package
For additional client configurations including different transport options (HTTP, SSE, Streamable HTTP), see client-config.json.example.
This file includes configurations for:
/mcp JSON-RPC with legacy /sse//messages clients)Fine-tune which operations the AI can perform by editing your config:
Usable instead of a JSON config file, or alongside one:
Environment variables override the config file key by key, so the env
block in your MCP client's configuration wins over whatever file the
server discovers. A variable only takes part when it is actually set, so
setting one leaves the rest of your file alone. Full order: direct
parameters > environment > config file > defaults.
Every tool can be toggled individually via JOPLIN_TOOL_<NAME>=true|false. These take precedence over config file settings.
| Env var | Default |
|---|---|
JOPLIN_TOOL_FIND_NOTES | true |
JOPLIN_TOOL_FIND_NOTES_WITH_TAG | true |
JOPLIN_TOOL_FIND_NOTES_IN_NOTEBOOK | true |
JOPLIN_TOOL_FIND_IN_NOTE | true |
JOPLIN_TOOL_GET_ALL_NOTES | false |
JOPLIN_TOOL_GET_NOTE | true |
JOPLIN_TOOL_GET_LINKS | true |
JOPLIN_TOOL_CREATE_NOTE | true |
JOPLIN_TOOL_UPDATE_NOTE | true |
JOPLIN_TOOL_EDIT_NOTE | true |
JOPLIN_TOOL_DELETE_NOTE | false |
JOPLIN_TOOL_LIST_NOTEBOOKS | true |
JOPLIN_TOOL_CREATE_NOTEBOOK | true |
JOPLIN_TOOL_UPDATE_NOTEBOOK | false |
JOPLIN_TOOL_DELETE_NOTEBOOK | false |
JOPLIN_TOOL_LIST_TAGS | true |
JOPLIN_TOOL_CREATE_TAG | true |
JOPLIN_TOOL_UPDATE_TAG | false |
JOPLIN_TOOL_DELETE_TAG | false |
JOPLIN_TOOL_GET_TAGS_BY_NOTE | true |
JOPLIN_TOOL_TAG_NOTE | true |
JOPLIN_TOOL_UNTAG_NOTE | true |
JOPLIN_TOOL_PING_JOPLIN | true |
JOPLIN_TOOL_RESTORE_FROM_TRASH | true |
JOPLIN_TOOL_IMPORT_FROM_FILE | false |
| Env var | Default | Description |
|---|---|---|
JOPLIN_NOTEBOOK_ALLOWLIST | (not set) | Comma-separated list of notebook patterns (e.g., Work,Projects/*,!Projects/Secret). Not set means no restriction; set but empty denies every notebook |
The server supports both STDIO and HTTP transports:
Note: Claude Desktop currently uses STDIO transport and does not consume HTTP/SSE configs directly. The following example applies to clients that support network transports.
| Option | Default | Description |
|---|---|---|
token | required | Joplin API authentication token |
host | localhost | Joplin server hostname |
port | 41184 | Joplin Web Clipper port |
timeout | 30 | Request timeout in seconds |
verify_ssl | false | SSL certificate verification |
| Option | Default | Description |
|---|---|---|
tools.create_note | true | Allow creating new notes |
tools.update_note | true | Allow modifying existing notes |
tools.edit_note | true | Allow precision edits (find/replace, append, prepend) |
tools.delete_note | false | Allow deleting notes (disabled by default β destructive) |
tools.create_notebook | true | Allow creating new notebooks |
tools.update_notebook | false | Allow modifying notebook titles and emoji icons |
tools.delete_notebook | false | Allow deleting notebooks (disabled by default β destructive) |
tools.create_tag | true | Allow creating new tags |
tools.update_tag | false | Allow modifying tag titles |
tools.delete_tag | false | Allow deleting tags (disabled by default β destructive) |
tools.tag_note | true | Allow adding tags to notes |
tools.untag_note | true | Allow removing tags from notes |
tools.restore_from_trash | true | Allow restoring soft-deleted notes or notebooks |
tools.find_notes | true | Allow text search across notes (with task filtering) |
tools.find_notes_with_tag | true | Allow finding notes by tag (with task filtering) |
tools.find_notes_in_notebook | true | Allow finding notes by notebook (with task filtering) |
tools.find_in_note | true | Allow regex search within a single note |
tools.get_all_notes | false | Allow getting all notes (disabled by default - can fill context window) |
tools.get_note | true | Allow getting specific notes |
tools.get_note_resources | true | Allow reading a note's resources and their OCR text |
tools.get_links | true | Allow extracting links to other notes |
tools.list_notebooks | true | Allow listing all notebooks |
tools.list_tags | true | Allow listing all tags |
tools.get_tags_by_note | true | Allow getting tags for specific notes |
tools.ping_joplin | true | Allow testing server connectivity |
tools.import_from_file | false | Allow importing files/directories (MD, HTML, CSV, TXT, JEX) |
| Option | Default | Description |
|---|---|---|
notebook_allowlist | null | List of notebook patterns to allow access to. null = no restriction. Supports gitignore-style patterns: exact names, * wildcards, ** recursive, ! negation |
| Option | Default | Description |
|---|---|---|
content_exposure.search_results | "preview" | Content visibility in all search and listing results: "none", "preview", "full" |
content_exposure.individual_notes | "full" | Content visibility for individual notes: "none", "preview", "full" |
content_exposure.max_preview_length | 300 | Maximum length of content previews (characters) |
content_exposure.smart_toc_threshold | 2000 | Notes longer than this show a table of contents instead of the full body |
content_exposure.enable_smart_toc | true | Turn the table-of-contents fallback off to always return full note bodies |
Lowering max_preview_length and smart_toc_threshold, or setting
search_results to "none", is the main way to cut how many tokens a
session spends. See docs/content-privacy.md.
Run the MCP server in a container. Default transport is HTTP for broad compatibility; switch via environment variables.
-e MCP_TRANSPORT=sse-e MCP_TRANSPORT=streamable-http-e MCP_TRANSPORT=stdioExample (SSE):
The container listens on 0.0.0.0:8000 by default. If exposing publicly, place behind a reverse proxy and terminate TLS there. For SSE, ensure proxy keep-alives and buffering are configured appropriately.
src/joplin_mcp/ - Main package directory
fastmcp_server.py - Server implementation with 26 tools and Pydantic validation typesconfig.py - Configuration management (including notebook allowlist)notebook_utils.py - Notebook path resolution, allowlist matching, and cachingserver.py - Server entrypoint (module and CLI)tools/ - Tool implementations (notes, notebooks, tags)ui_integration.py - UI integration utilitiesdocs/ - Documentation (troubleshooting, privacy controls, enhancement proposals)tests/ - Unit test suitetests/e2e/ - End-to-end tests against a real Joplin Desktop (3.x) via the Web Clipper API; see "Running Tests"Test your connection:
You should see:
The E2E suite talks to a real Joplin Desktop via the Web Clipper API and exercises every tool including notebook allowlist enforcement. If JOPLIN_HOST:JOPLIN_PORT is unreachable the suite skips itself, so it's safe to run alongside the unit tests. Requires Joplin 3.x (the trash schema introduced in 3.0 β earlier versions fail with no such column: deleted_time).
| Tool | Permission | Description |
|---|---|---|
| Finding Notes | ||
find_notes | Read | Full-text search across all notes (supports task filtering; trash=True with query="*" lists trashed notes) |
find_notes_with_tag | Read | Find notes with specific tag (supports task filtering) |
find_notes_in_notebook | Read | Find notes in specific notebook (supports task filtering) |
get_all_notes | Read | Get all notes, most recent first (disabled by default) |
get_note | Read | Get specific note by ID |
find_in_note | Read | Regex search within a single note (paginated matches & context, multiline anchors on by default) |
get_links | Read | Extract links to other notes from a note |
get_note_resources | Read | List a note's resources (images, PDFs, attachments) and read their OCR text |
| Managing Notes | ||
create_note | Write | Create new notes |
update_note | Update | Modify existing notes (incl. moving between notebooks) |
edit_note | Update | Precision edit note content (find/replace, append, prepend) |
delete_note | Delete | Remove notes |
| Managing Notebooks | ||
list_notebooks | Read | Browse all notebooks |
create_notebook | Write | Create new notebooks under an optional parent (by name or path), optionally with an emoji icon |
update_notebook | Update | Rename, change emoji icon, or move a notebook under another parent (or to top-level with parent_name="/") |
delete_notebook | Delete | Remove notebooks |
| Managing Tags | ||
list_tags | Read | View all available tags |
create_tag | Write | Create new tags |
update_tag | Update | Modify tag titles |
delete_tag | Delete | Remove tags |
get_tags_by_note | Read | List tags on specific note |
| Tag-Note Relationships | ||
tag_note | Update | Add one or more tags to one or more notes (accepts lists) |
untag_note | Update | Remove one or more tags from one or more notes (accepts lists) |
| Trash Management | ||
restore_from_trash | Update | Restore a soft-deleted note or notebook (pass item_type='note' or 'notebook') |
| Import Tools | ||
import_from_file | Write | Import files/directories (MD, HTML, CSV, TXT, JEX) |
| System Tools | ||
ping_joplin | Read | Test connectivity |
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/joplin-mcp)<a href="https://allmcps.com/mcp/joplin-mcp"><img src="https://allmcps.com/api/badge/joplin-mcp?style=directory" alt="Joplin Mcp on AllMCPs" /></a>