From joplin-mcp
Orchestrates Joplin MCP tools for note, notebook, and tag management. Guides setup via API token, editing vs updating notes, reading long content, ID handling, bulk tagging, and workflows.
npx claudepluginhub alondmnt/joplin-mcp --plugin joplin-mcpThis skill uses the workspace's default tool permissions.
This skill covers non-obvious tool interactions. Tool parameters are already in the tool schemas — don't restate them here.
Manages notes in self-hosted Memos service via API scripts: create, list, search, update, delete, tag, attach files, and organize. Activates on save-to-memos, search-notes, or note-taking requests.
Manages Obsidian vaults: search, create, edit, move Markdown notes; handle YAML frontmatter, wikilinks, backlinks, daily notes, Zettelkasten setup, and sync via obsidian-cli.
Automates repetitive Obsidian vault tasks via CLI, shell commands, and scripts: batch-creates notes, bulk-updates frontmatter, runs maintenance, opens notes, navigates programmatically, integrates with external tools.
Share bugs, ideas, or general feedback.
This skill covers non-obvious tool interactions. Tool parameters are already in the tool schemas — don't restate them here.
If Joplin MCP tools (e.g., ping_joplin) aren't available, the server isn't connected. Follow these steps to configure it automatically:
Ask the user for their Joplin API token. Tell them where to find it:
Open Joplin Desktop → Tools → Options → Web Clipper → copy the Authorization token.
Write .mcp.json to the project root with the token. Use the Write tool to create the file:
{
"mcpServers": {
"joplin-mcp": {
"command": "uvx",
"args": ["--from", "joplin-mcp", "joplin-mcp-server"],
"env": {
"JOPLIN_TOKEN": "<paste_token_here>"
}
}
}
}
This file is already covered by .gitignore (.* rule) so it won't be committed.
Tell the user to restart Claude Code. The MCP server is loaded at startup, so a restart is required for the tools to appear.
After restart, call ping_joplin to verify the connection.
edit_note — find/replace, append, prepend. Use this for partial changes.update_note(body=...) — replaces the entire body. Only use when you intend to rewrite the whole note.If you need to change a paragraph, fix a typo, or append a section: always use edit_note.
get_note returns a table of contents (not content) when a note exceeds ~50 lines. To read the actual content:
get_note(note_id) — see the TOC and line countget_note(note_id, section="Section Name") — extract a specific sectionget_note(note_id, start_line=1, line_count=50) — sequential reading by line rangeget_note(note_id, force_full=True) — force full content (large context cost)| Parameter | Accepts |
|---|---|
note_id | 32-char hex ID only |
notebook_name | Human-readable name (e.g., "Work") |
tag_name | Human-readable name (e.g., "important") |
parent_id | 32-char hex ID of parent notebook |
Search results return IDs — use those IDs in subsequent calls.
tag_note fails if the tag doesn't exist. Before tagging multiple notes:
create_tag("my-tag") — create it first (idempotent if it already exists)tag_note(note_id, "my-tag") for each noteget_note(id) # see TOC + line count
get_note(id, section="Target") # read the section you need
edit_note(id, old_string="...", new_string="...") # surgical edit
list_notebooks() # find parent notebook ID
create_notebook("Sub", parent_id="<parent_hex_id>")
create_note("Title", notebook_name="Sub", body="...")
create_tag("project-x") # ensure tag exists first
find_notes("project x") # get note IDs
tag_note(id1, "project-x") # tag each result
tag_note(id2, "project-x")
list_notebooks() # find target notebook ID
update_note(note_id, notebook_id="<target_hex_id>")