Skip to content
joplin-publisher logo

Joplin Publisher Skill

joplin-publisher

Import markdown documents and mermaid diagrams into Joplin using the Joplin CLI.

SKILL.md

Full skill instructions

Joplin Publisher Skill

Purpose

Import markdown documents and mermaid diagrams into Joplin using the Joplin CLI.

When to Use

  • Exporting research to Joplin notebooks
  • Creating documentation in Joplin
  • Generating diagrams for Joplin knowledge base
  • Syncing analysis results to Joplin

Overview

Joplin uses a database-backed system. Content is imported via the Joplin CLI or API. This skill focuses on CLI-based imports.

Prerequisites

Install Joplin CLI

# Using npm
npm install -g joplin

# Verify installation
joplin version

Configure Joplin CLI

# Set sync target (if needed)
joplin config sync.target 2  # Filesystem
joplin config sync.2.path /​path/​to/​sync/​folder

# Or connect to Joplin Server/​Cloud
joplin config sync.target 9
joplin config sync.9.path https://your-joplin-server.com
joplin config sync.9.username your-username

Joplin CLI Commands

List Notebooks

joplin ls /          # List top-level notebooks
joplin ls /​Notebook  # List notes in notebook

Create Notebook

joplin mkbook "Notebook Name"
joplin mkbook "Parent/​Child"  # Nested notebook

Import Markdown

# Import single file to notebook
joplin import /​path/​to/​file.md --notebook "Notebook Name"

# Import directory
joplin import /​path/​to/​folder --notebook "Notebook Name"

# Import with format specification
joplin import /​path/​to/​file.md --format md --notebook "Notebook Name"

Create Note Directly

# Create note from stdin
echo "# Title

Content" | joplin mknote "Note Title" --notebook "Notebook Name"

# Create from file content
cat file.md | joplin mknote "Note Title" --notebook "Notebook Name"

Publishing Workflow

Step 1: Validate Notebook Exists

import subprocess

def notebook_exists(notebook: str) -> bool:
    """Check if a Joplin notebook exists."""
    result = subprocess.run(
        ["joplin", "ls", "/"],
        capture_output=True,
        text=True
    )
    notebooks = result.stdout.strip().split('
')
    return notebook in notebooks

def create_notebook_if_missing(notebook: str):
    """Create notebook if it doesn't exist."""
    if not notebook_exists(notebook):
        subprocess.run(
            ["joplin", "mkbook", notebook],
            check=True
        )

Step 2: Prepare Content

def prepare_markdown(
    title: str,
    content: str,
    tags: list = None
) -> str:
    """
    Prepare markdown content for Joplin import.

    Joplin supports YAML frontmatter for metadata.
    """
    lines = [f"# {title}", ""]

    if tags:
        lines.extend([
            "---",
            f"tags: {', '.join(tags)}",
            "---",
            ""
        ])

    lines.append(content)
    return '
'.join(lines)

Step 3: Write Temporary File

import tempfile
from pathlib import Path

def write_temp_markdown(content: str, filename: str) -> Path:
    """Write content to a temporary markdown file."""
    temp_dir = Path(tempfile.mkdtemp())
    file_path = temp_dir / f"{filename}.md"
    file_path.write_text(content, encoding='utf-8')
    return file_path

Step 4: Import to Joplin

def import_to_joplin(
    file_path: Path,
    notebook: str
) -> bool:
    """Import markdown file to Joplin notebook."""
    result = subprocess.run(
        [
            "joplin", "import",
            str(file_path),
            "--notebook", notebook
        ],
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        raise JoplinImportError(f"Import failed: {result.stderr}")

    return True

Complete Publishing Function

def publish_to_joplin(
    notebook: str,
    title: str,
    content: str,
    tags: list = None
) -> bool:
    """
    Publish markdown content to a Joplin notebook.

    Args:
        notebook: Target notebook name
        title: Note title
        content: Markdown content (can include mermaid)
        tags: Optional list of tags

    Returns:
        True if successful
    """
    # Ensure notebook exists
    create_notebook_if_missing(notebook)

    # Prepare content
    full_content = prepare_markdown(title, content, tags)

    # Write to temp file
    temp_file = write_temp_markdown(full_content, title)

    try:
        # Import to Joplin
        import_to_joplin(temp_file, notebook)
        return True
    finally:
        # Cleanup
        temp_file.unlink()
        temp_file.parent.rmdir()

Document Formats

Basic Note

# Note Title

Content goes here.

## Section

More content.

Note with Tags

# Note Title


Content here.

Note with Mermaid

Joplin supports mermaid diagrams natively in markdown:

# System Architecture

```mermaid
flowchart TD
    A[Client] --> B[Server]
    B --> C[(Database)]

Description

The system consists of...


## Notebook Organization

### Flat Structure

Notebooks/ ├── Research ├── Projects ├── Meetings └── Archive


### Nested Structure

Notebooks/ ├── Work/ │ ├── Project Alpha │ └── Project Beta ├── Personal/ │ ├── Notes │ └── Ideas └── Archive/


Create nested notebooks:
```bash
joplin mkbook "Work"
joplin mkbook "Work/​Project Alpha"

Usage Examples

Publish Research Note

publish_to_joplin(
    notebook="Research",
    title="API Design Patterns",
    content="""
## Overview

Key findings from API design research.

## REST Best Practices

1. Use nouns for resources
2. Use HTTP methods correctly
3. Version your API

## GraphQL Considerations

- Schema-first design
- Query optimization
""",
    tags=["api", "research", "design"]
)

Publish Diagram

publish_to_joplin(
    notebook="Architecture",
    title="System Overview Diagram",
    content="""
## Architecture Diagram

```mermaid
flowchart TB
    subgraph Frontend
        A[Web App]
        B[Mobile App]
    end

    subgraph Backend
        C[API Server]
        D[Worker]
    end

    subgraph Data
        E[(PostgreSQL)]
        F[(Redis)]
    end

    A --> C
    B --> C
    C --> E
    C --> F
    D --> E

Components

ComponentTechnologyPurpose
Web AppReactUser interface
API ServerFastAPIREST API
WorkerCeleryBackground jobs
""",
tags=["architecture", "diagram"]

)


### Publish Meeting Notes
```python
publish_to_joplin(
    notebook="Meetings/​2024",
    title="Project Sync - Jan 15",
    content="""
## Attendees
- Alice
- Bob
- Charlie

## Agenda
1. Sprint review
2. Blockers
3. Next steps

## Notes

### Sprint Review
- Feature X completed
- Bug Y in progress

### Blockers
- Waiting on API access

### Action Items
- [ ] Alice: Follow up on API access
- [ ] Bob: Complete bug fix
- [ ] Charlie: Update documentation
""",
    tags=["meeting", "project-alpha"]
)

Batch Import

For importing multiple documents:

def batch_import(
    notebook: str,
    documents: list[dict]
) -> dict:
    """
    Import multiple documents to Joplin.

    Args:
        notebook: Target notebook
        documents: List of {title, content, tags} dicts

    Returns:
        {success: int, failed: int, errors: list}
    """
    results = {"success": 0, "failed": 0, "errors": []}

    for doc in documents:
        try:
            publish_to_joplin(
                notebook=notebook,
                title=doc["title"],
                content=doc["content"],
                tags=doc.get("tags", [])
            )
            results["success"] += 1
        except Exception as e:
            results["failed"] += 1
            results["errors"].append({
                "title": doc["title"],
                "error": str(e)
            })

    return results

Error Handling

class JoplinError(Exception):
    """Base exception for Joplin operations."""
    pass

class JoplinNotInstalledError(JoplinError):
    """Joplin CLI not found."""
    pass

class JoplinImportError(JoplinError):
    """Failed to import content."""
    pass

class NotebookNotFoundError(JoplinError):
    """Notebook does not exist."""
    pass

def check_joplin_installed():
    """Verify Joplin CLI is available."""
    result = subprocess.run(
        ["joplin", "version"],
        capture_output=True
    )
    if result.returncode != 0:
        raise JoplinNotInstalledError(
            "Joplin CLI not found. Install with: npm install -g joplin"
        )

Joplin API Alternative

For more control, use the Joplin Data API:

import requests

class JoplinAPI:
    def __init__(self, token: str, port: int = 41184):
        self.base_url = f"http://localhost:{port}"
        self.token = token

    def create_note(
        self,
        title: str,
        body: str,
        parent_id: str = None
    ) -> dict:
        """Create a note via Joplin API."""
        response = requests.post(
            f"{self.base_url}/​notes",
            params={"token": self.token},
            json={
                "title": title,
                "body": body,
                "parent_id": parent_id
            }
        )
        response.raise_for_status()
        return response.json()

Enable the API in Joplin Desktop: Options → Web Clipper → Enable

Checklist

Before publishing:

  • Joplin CLI is installed and configured
  • Target notebook exists or will be created
  • Content is valid markdown
  • Mermaid diagrams use correct syntax
  • Tags are properly formatted
  • Sync is configured (if using cloud/​server)

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View