SkillAgentSearch skills...

joplin-mcp

Joplin MCP server for AI assistants. Manage notes, notebooks, tags, search, and sync through 17 MCP tools. Docker/HTTP deployment.

Install / Use

claude mcp add gelse -- npx -y github:gelse/joplin-mcp

If the server publishes to npm under a different name, use that package instead — check the repo README.

About this skill
🔌

MCP Server

Model Context Protocol server

Quality Score

84/100

Category

Operations

Supported Platforms

Claude Code
Claude Desktop

Our assessment of joplin-mcp

joplin-mcp scores 84/100 on our quality scale, 464th of 633 Operations skills we index.

Its MCP Server is 37 KB long, well organised into 67 sections with 15 code examples: a thorough specification that gives an agent plenty to work with.

It has 10 GitHub stars, so there is little community track record yet; judge it on its content.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
4/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 4 days ago, so joplin-mcp is actively maintained.
  • It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
  • Its trust signals score 97/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

joplin-mcp compared with similar skills

All 4 of these similar skills score higher than joplin-mcp; compare them before choosing.

SkillScoreStarsUpdatedFormat
joplin-mcp (this skill)by gelse84104d agoMCP Server
Agent-Reachby Panniantong10087.5k16d agoCLAUDE.md
headroomby headroomlabs-ai10074.2ktodayCLAUDE.md
rufloby ruvnet10073.7ktodayCLAUDE.md
CowAgentby zhayujie10047.2ktodayCLAUDE.md

Frequently asked questions

How do I install joplin-mcp?
Run claude mcp add gelse -- npx -y github:gelse/joplin-mcp. The install tabs above show the steps for each supported agent.
Which AI agents does joplin-mcp work with?
It is written for Claude Code and Claude Desktop, as a MCP Server file. Other agents that read the same format can often use it too.
Is joplin-mcp safe to use?
It is MIT-licensed and scores 97/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is joplin-mcp still maintained?
The repository was last updated 4 days ago, so joplin-mcp is actively maintained.

Joplin API MCP Server

An MCP (Model Context Protocol) server that exposes Joplin's note-taking functionality — notes, folders, tags, search, and sync — to AI assistants via 17 tools.

tl;dr / Quick Start

Docker (recommended)

The recommended deployment uses the published container image. No repository clone required.

docker run -d \
  --name joplin-mcp \
  --restart unless-stopped \
  -p 127.0.0.1:3000:3000 \
  -v joplin_data:/home/joplin/.config/joplin \
  -e JOPLIN_SERVER_URL=https://joplin.example.com/ \
  -e JOPLIN_USERNAME=your-email@example.com \
  -e JOPLIN_PASSWORD=your-password \
  ghcr.io/gelse/joplin-mcp:latest

Bleeding-edge builds: To test the latest (unreleased) build, use ghcr.io/gelse/joplin-mcp:latest-testing instead of :latest.

Tip: If your Joplin Server is running on the same host machine, use host.docker.internal as the hostname in JOPLIN_SERVER_URL (e.g., https://host.docker.internal:22300) so the container can reach it over Docker's built-in DNS.

Environment Variables

| Variable | Required | Default | Description | | ------------------------ | -------- | ------- | ----------------------------------------------------------------- | | JOPLIN_SERVER_URL | Yes | — | Joplin Server URL (e.g., https://joplin.example.com/) | | JOPLIN_USERNAME | Yes | — | Joplin Server username/email | | JOPLIN_PASSWORD | Yes | — | Joplin Server password | | JOPLIN_API_TOKEN | No | — | Joplin Data API token (auto-extracted when unset) | | JOPLIN_DATA_API_PORT | No | 41184 | Internal Data API listen port (rarely changed) | | LOG_LEVEL | No | info | Log level: debug, info, warn, error, silent | | SYNC_INTERVAL_SECONDS | No | 300 | Periodic sync interval in seconds | | MCP_HOST_PORT | No | 3000 | Host-side MCP port (mapped via -p 127.0.0.1:MCP_HOST_PORT:3000) | | JOPLIN_MASTER_PASSWORD | No | — | E2EE master password (leave empty to skip encryption) |

Note: JOPLIN_CORE_URL is no longer an operator-facing variable — the entrypoint sets it internally to http://127.0.0.1:<JOPLIN_DATA_API_PORT> (default 41184).

MCP Client Configuration

The joplin-mcp container exposes an HTTP endpoint (not stdio). Configure your MCP client to connect via URL:

{
  "mcpServers": {
    "joplin": {
      "url": "http://localhost:3000/mcp"
    }
  }
}

⚠️ End-to-End Encryption (E2EE)

If you have End-to-End Encryption (E2EE) enabled on your Joplin Server, the container must know the master password before it can encrypt notes for upload. Without it, writes appear to succeed locally but silently fail to reach the server — and the sync process will misleadingly report SYNC_PASS.

What E2EE means

When E2EE is enabled, Joplin encrypts all note content on the client before sending it to the server. The server only ever sees ciphertext — decryption happens client-side using the master password. This project's container acts as such a client, so it must have the master password configured.

Setting the master password

Preferred (declarative) — set via environment variable:

Add JOPLIN_MASTER_PASSWORD to your .env file (or pass it via docker compose run / docker run -e). The entrypoint configures the password on every fresh container start, before the initial sync.

Fallback (manual) — configure after container start:

# Set the master password inside the joplin-mcp container
docker exec joplin-mcp joplin config encryption.masterPassword 'THE_PASSWORD'

# Restart so the new config is picked up
docker restart joplin-mcp

Replace THE_PASSWORD with the same master password used when enabling E2EE on Joplin Server (or the one you chose if you enabled it from the CLI).

Tip: The password is persisted in the joplin_data Docker volume. When using the environment variable, the entrypoint re-applies it on every start — no manual step is needed after the first run.

⚠️ Warning: joplin e2ee decrypt does NOT persist the password

The command joplin e2ee decrypt -p 'PASSWORD' decrypts data for the current session only and does not store the password for future sync operations. Using it as your setup step will cause encrypted items to silently fail to upload on subsequent syncs. Always use joplin config encryption.masterPassword instead.

How to tell if E2EE is the problem

If you notice notes are missing from Joplin Server despite the container reporting SYNC_PASS, check whether E2EE is enabled on the server and whether the master password has been configured in the container.


Detailed How-To

Docker

Prerequisites

  • Docker and Docker Compose installed on your system
  • The .env.example file copied to .env and configured with your Joplin Server credentials

The deployment uses a single combined Dockerfile (Dockerfile.combined) orchestrated via docker-compose.yml.

Building

# Build the combined container
docker compose build

Running

docker compose up -d   # starts the combined joplin-mcp container

Viewing Logs

docker compose logs -f              # combined container logs
docker compose logs -f joplin-mcp   # same (single service)

Stopping

docker compose down

How It Works

  • Single container: joplin-mcp — one combined container runs the Joplin CLI + Data API (loopback-only), a bash periodic-sync loop, and the Node.js MCP HTTP server
  • Multi-stage builds: Dockerfile.combined uses node:22-bookworm-slim with separate build and production stages
  • Non-root user: joplin user (uid 1001) for all processes
  • Persistent volume: joplin_data volume mounted at /home/joplin/.config/joplin stores the Joplin profile and SQLite database
  • Loopback-only Data API: The Data API binds to 127.0.0.1:41184 inside the container — no socat, no port proxy
  • Published port: Only port 3000 (MCP) is mapped to the host via 127.0.0.1:${MCP_HOST_PORT:-3000}:3000
  • Healthchecks: The container healthcheck probes both 127.0.0.1:41184/ping (Data API) and 127.0.0.1:3000/health (MCP server)
  • Graceful shutdown: The entrypoint traps SIGTERM, drains the sync loop process group, stops the MCP server, performs a final sync, and exits cleanly

Testing

A dedicated Dockerfile.tests and test service in docker-compose.yml allow running the test suite in a container:

# Build the test image
docker build -f Dockerfile.tests -t joplin-api-tests .

# Run tests
docker run --rm joplin-api-tests

# Or via docker compose (requires --profile test since the test service uses profiles)
docker compose --profile test run --rm tests

Tests use Vitest with v8 coverage (thresholds: 70% statements, 60% branches, 70% functions, 70% lines) and output JUnit XML reports to ./reports/. When running via docker compose, the ./reports directory is mounted into the container so reports persist on the host.

The test suite does not require a running Joplin instance — unit tests use mocks, and integration tests are skipped when the Joplin Data API is unavailable.

Container Integration Tests

End-to-end tests that run the full MCP stack in a Docker container using the integration-test stack (docker-compose.test.yml), built from Dockerfile.combined.

Prerequisites

  • Docker and Docker Compose v2

Running

make test-integration
# or
./scripts/run-integration-tests.sh

What it tests

  • MCP connection and tool discovery (17 tools)
  • Note CRUD via MCP tools
  • Folder CRUD via MCP tools
  • Search and tag operations
  • Error handling and validation

Architecture

Tests connect to joplin-mcp via @modelcontextprotocol/sdk StreamableHTTP transport. The combined container runs with dummy sync credentials — no real Joplin Server is needed. The API token is auto-extracted from the Joplin CLI config at startup.

Reports

  • JUnit XML: reports/container/junit.xml
  • Container logs: reports/container/*.log

CI/CD

Four GitHub Actions workflows automate testing and releases:

| Workflow | Trigger | Runner | Description | | ------------------------------------------------------------------ | ----------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | unit-tests.yml | Push / PR to main | Ubuntu (native) | Installs dependencies via pnpm, runs pnpm test | | integration-tests.yml | PRs to main | Ubuntu (native) | Runs container integration tests via scripts/run-integration-tests.sh | | publish-testing.yml | Push to testing | Ubuntu (native) | Runs unit and integration tests, then uploads a Docker image to ghcr.io/gelse/joplin-mcp:latest-testing (upload only if both test jobs pass — no GitHub release) | | release.yml | Release published / manual dispatch | Ubuntu (native) | Verifies lockfile reproducibility, then builds Dockerfile.combined and pushes to ghcr.io/gelse/joplin-mcp with semver + latest tags |

The publish-testing.yml workflow gates the image upload behind both the unit and integration test jobs — the publish job runs only when both test jobs succeed. It uploads a linux/amd64 image tagged as latest-testing; this is an upload, not a release, so it does not create a GitHub release or use versioned tags. The release.yml workflow remains the sole owner of versioned tags and the latest tag.

The release workflow builds a single linux/amd64 image and tags it as {{version}}, {{major}}.{{minor}}, {{major}}, and latest (when appropriate). A pre-build step runs pnpm install --frozen-lockfile to confirm the lockfile is reproducible before the Docker build begins.

The release workflow also supports workflow_dispatch for manual triggers — useful for the initial GHCR publish when the package does not yet exist (semver tags won't resolve on manual dispatch, but the latest fallback tag ensures the image is always pushed with at least one valid tag).

Branch protection: If branch protection rules are configured on main, add the required status checks unit-tests and integration-tests (the previous test job name no longer exists).

Architecture

Single-Container Deployment (Docker)

graph TD
    A[AI Client] -->|"MCP HTTP (port 3000)"| B[joplin-mcp container]
    subgraph "joplin-mcp (single container)"
        B[MCP HTTP Server :3000]
        C[Joplin Data API :41184] -->|"read/write"| D[

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars10
CategoryOperations
Updated4d ago
Forks0

Languages

TypeScript

Trust signals

97/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

1 info