joplin-md-sync
Safe two-way synchronization between the local Joplin desktop application and an ordinary directory of Markdown files — built primarily for autonomous coding agents (deterministic JSON output, stable exit codes, explicit conflict handling), and perfectly usable by humans.
Documentation: joplin-mcp.romancello.net
Safety first. The tool never overwrites divergent edits, never deletes anything without an explicit flag, never uses permanent deletion in Joplin, verifies every write after applying it, and journals every mutating run so interrupted syncs are recoverable.
diffnever mutates anything.
The test-backed definitions of these guarantees live in the contract catalog.
If you are an agent (or configuring one), start with AGENTS.md.
How it works
- Notes are plain
.mdfiles; each carries a one-line metadata header with the Joplin id, title, and tags. Notebooks are directories with a.joplin-folder.json. - Sync state (base snapshots for true three-way comparison) lives in
.joplin-sync/state.sqlite3inside the workspace — never committed to Git. - All communication uses the documented local Joplin Data API (Web Clipper service); Joplin's own database and sync targets are never touched.
Installation
Source, wheel, and zipapp installations require CPython >= 3.13 on Windows or Linux. Native release executables include Python and have no external runtime dependencies.
python -m pip install "joplin-md-sync==1.5.5"
# or: pipx install "joplin-md-sync==1.5.5"
# or download joplin-md-sync.pyz from a release and: python joplin-md-sync.pyz --help
Native GitHub Release assets:
| Platform | Architecture | Asset |
|---|---|---|
| Linux | AMD64 | joplin-md-sync-linux-amd64 |
| Linux | ARM64 | joplin-md-sync-linux-arm64 |
| Windows | AMD64 | joplin-md-sync-windows-amd64.exe |
On Linux, mark the downloaded executable as executable before running it:
chmod +x joplin-md-sync-linux-amd64
./joplin-md-sync-linux-amd64 version
From a checkout, everything is driven by the Makefile:
make venv # runtime venv/ with the CLI installed (venv/bin/joplin-md-sync)
make venv-dev # Linux quality tools (ruff, mypy, Bandit, pip-audit)
make venv-test # cross-platform pytest tools
make venv-package # cross-platform PyInstaller and build tools
make check # lint + typecheck + full test suite
make freeze-check # verify all generated dependency locks are current
make test-live # opt-in real-Joplin MCP + GPT Actions suites; reads ./token
make test TEST_WORKERS=8 # override automatic parallel test workers
make package # wheel, sdist, .pyz, current-platform executable, checksums
make help # all targets
The version's single source is the root .version file; runtime
dependencies are declared in pyproject.toml (none by design). Direct tools are
pinned to their latest stable compatible releases in purpose-specific quality,
test, package, and docs groups. The four committed pip-compile locks include
SHA-256 hashes and are checked for drift in CI; Windows jobs install only the
cross-platform test or package lock they need.
Five-minute quick start
- In Joplin: Tools > Options > Web Clipper — enable the service, copy the authorization token.
-
export JOPLIN_TOKEN=<your token> # Windows: set JOPLIN_TOKEN=... joplin-md-sync init --root ./notes joplin-md-sync pull --root ./notes
The token is the only required configuration: the default endpointhttp://127.0.0.1:41184is built in (override withJOPLIN_BASE_URL,JOPLIN_PORT,--base-url, or--portwhen needed). - Edit files under
./notes, then:joplin-md-sync diff --root ./notes joplin-md-sync push --root ./notes --dry-run joplin-md-sync push --root ./notes
- If both sides changed the same note, you get exit code 2 and a conflict
bundle:
joplin-md-sync conflicts list/conflicts resolve ID --take-local|--take-remote|--merged-file PATH.
Agent notes repository template
examples/agent-notes-repository/ is a
copyable starter repository for users who want an agent to work on their
Joplin notes as ordinary Markdown files. It includes:
- a repository-specific
AGENTS.mdwith the guarded pull/edit/diff/dry-run/push workflow and an optional MCP setup path; - a human runbook that starts with enabling Web Clipper in Joplin Desktop and storing its token outside the sync workspace;
- a
.gitignorefor credentials, the local standalone binary, and all sync state; and - a standard-library installer that downloads the latest compatible native
release into
.tools/, verifiesSHA256SUMS.txt, and validates the binary before replacing an existing installation.
Start with the template's README, not the development instructions for this source checkout.
MCP and ChatGPT Actions service
One foreground joplin-md-sync process exposes MCP and authenticated REST
Actions from the same tool registry on different URIs. It starts while Joplin
is offline and recovers on later calls without a restart.
The Actions token is mandatory when Actions are enabled and is reloaded from a
protected file. MCP bearer authentication remains optional through a separate
--auth-token-file. The headless installer always installs both APIs as one
joplin-md-sync.service; it generates separate mandatory Actions and MCP
tokens, stores them in protected files, and reports only their paths after a
successful install. It never creates a separate Actions service.
Use service installation and operations for Linux, Windows, credentials, URI isolation, and live tests; MCP API for the tool contract; and the single ChatGPT Actions end-to-end setup for endpoint validation, schema export, GPT Instructions, editor configuration, and acceptance testing.
Architecture overview
cli -> planner (pure three-way classification: base/local/remote)
-> executor (guard -> apply -> verify -> commit base, journaled)
api: stdlib urllib client for the Joplin Data API (pagination, retries)
mcp/actions: two HTTP transports -> shared registry/executor -> Joplin service -> api
state: SQLite base snapshots, conflicts, tombstones, run journal
workspace: scanning, atomic writes, backups, quarantine, cross-platform lock
Details in Architecture.
Supported / not supported (v1)
| Supported | Out of scope |
|---|---|
| notes, notebooks (nested), tags, binary attachments | editing settings, revisions, or encryption state |
| two-way sync with conflict bundles | Nextcloud/WebDAV or any direct sync target |
| resource download/upload/edit through MCP | replacing Joplin's own device sync |
| crash recovery, backups, quarantine | automatic text merging (only explicit --merged-file) |
| Windows + Linux, Python 3.13/3.14; MCP daemon | filesystem watch mode, native mobile CLI, self-update, permanent note/notebook deletion |
Versioning
Semantic versioning; Git tags vX.Y.Z with GitHub releases carrying the
wheel, sdist, .pyz, native executables, and SHA-256 checksums.
The same wheel and sdist are published to PyPI through Trusted Publishing.
joplin-md-sync update-check --json compares the installed version against
the latest stable release
(exit 8 when outdated). JSON output, exit codes, and the state schema are
versioned and stable across patch releases. See
CHANGELOG.md.
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file joplin_md_sync-1.5.5.tar.gz.
File metadata
- Download URL: joplin_md_sync-1.5.5.tar.gz
- Upload date:
- Size: 200.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b69969705593d0f7ae8a9919f701edaf666729fca8e60e9e2a0fd5dd386f7097
|
|
| MD5 |
25fe1a6e19aa37848e4205c54e3f135a
|
|
| BLAKE2b-256 |
a286602394b5382e66732f3445bc64676d6b0e672ef3927a34ad387cbaf864db
|
Provenance
The following attestation bundles were made for joplin_md_sync-1.5.5.tar.gz:
Publisher:
release.yml on kogeler/joplin-md-sync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
joplin_md_sync-1.5.5.tar.gz -
Subject digest:
b69969705593d0f7ae8a9919f701edaf666729fca8e60e9e2a0fd5dd386f7097 - Sigstore transparency entry: 2550146150
- Sigstore integration time:
-
Permalink:
kogeler/joplin-md-sync@9572a4612dd8b4d78ce24bcfea0c0a1bafc88454 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/kogeler
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9572a4612dd8b4d78ce24bcfea0c0a1bafc88454 -
Trigger Event:
push
-
Statement type:
File details
Details for the file joplin_md_sync-1.5.5-py3-none-any.whl.
File metadata
- Download URL: joplin_md_sync-1.5.5-py3-none-any.whl
- Upload date:
- Size: 99.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a0d032650e41c1d5df0cad5861ae2534eff8fe8bf721eb46e80e2da51cbe0e5
|
|
| MD5 |
1870baab10fb3ed08e542a047f286b1e
|
|
| BLAKE2b-256 |
027429287cb46f27c30d96a62ceff41ea150d0f39997d6aa85185b51f4b746ef
|
Provenance
The following attestation bundles were made for joplin_md_sync-1.5.5-py3-none-any.whl:
Publisher:
release.yml on kogeler/joplin-md-sync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
joplin_md_sync-1.5.5-py3-none-any.whl -
Subject digest:
7a0d032650e41c1d5df0cad5861ae2534eff8fe8bf721eb46e80e2da51cbe0e5 - Sigstore transparency entry: 2550146187
- Sigstore integration time:
-
Permalink:
kogeler/joplin-md-sync@9572a4612dd8b4d78ce24bcfea0c0a1bafc88454 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/kogeler
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9572a4612dd8b4d78ce24bcfea0c0a1bafc88454 -
Trigger Event:
push
-
Statement type: