Claude Code Plugins

Community-maintained marketplace

Feedback

joplin-publisher

@mindmorass/reflex
0
0

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

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name joplin-publisher
description Import markdown documents and mermaid diagrams into Joplin using the Joplin CLI.

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

Component Technology Purpose
Web App React User interface
API Server FastAPI REST API
Worker Celery Background 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)