# https://github.com/jomkz/second-brain-joplin Project Manual

Generated at: 2026-07-01 19:55:01 UTC

## Table of Contents

- [Project Overview and System Architecture](#page-overview)
- [MCP Tools Reference](#page-tools)
- [Development Workflow, Testing, and CI/CD](#page-dev-cicd)
- [Installation, Deployment, and PyPI Release](#page-deploy-release)

<a id='page-overview'></a>

## Project Overview and System Architecture

### Related Pages

Related topics: [MCP Tools Reference](#page-tools), [Installation, Deployment, and PyPI Release](#page-deploy-release)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md)
- [CHANGELOG.md](https://github.com/jomkz/second-brain-joplin/blob/main/CHANGELOG.md)
- [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md)
- [SECURITY.md](https://github.com/jomkz/second-brain-joplin/blob/main/SECURITY.md)
- [src/second_brain_joplin/__init__.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/__init__.py)
- [src/second_brain_joplin/server.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py)
- [src/second_brain_joplin/joplin_client.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/joplin_client.py)
- [.env.example](https://github.com/jomkz/second-brain-joplin/blob/main/.env.example)
</details>

# Project Overview and System Architecture

## Purpose and Scope

`second-brain-joplin` is a [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server that exposes a local [Joplin](https://joplinapp.org/) knowledge base to any MCP-capable AI client — Claude Code, Cursor, and similar tools. Source: [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md). It is the Joplin counterpart to [second-brain-mcp](https://github.com/noesskeetit/second-brain-mcp), which targets Obsidian. Source: [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md).

The project is currently at **v0.1.0 (bootstrap)** — the package installs, the server starts, and all five MCP tools are registered, but each one returns a `{"status": "not implemented"}` payload while the underlying `JoplinClient` methods (except `ping()`) remain stubbed. Source: [src/second_brain_joplin/__init__.py:3](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/__init__.py), [src/second_brain_joplin/server.py:34-58](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py). Real implementations are tracked in the [v0.2 milestone](https://github.com/jomkz/second-brain-joplin/milestone/2) and tracked as issues #5–#8 and #10 in the repository. Source: [src/second_brain_joplin/server.py:32](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py).

## High-Level Architecture

The runtime topology is a three-tier, fully local chain. The AI client never touches the Joplin REST API directly; it speaks MCP to this server, which then forwards requests to Joplin over HTTP on `localhost`. Source: [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md).

```mermaid
flowchart LR
    A["MCP client<br/>(Claude Code / Cursor)"]
    B["second-brain-joplin<br/>FastMCP server (stdio)"]
    C["Joplin Desktop<br/>Web Clipper REST API<br/>http://localhost:41184"]
    D[("Joplin notes<br/>& notebooks")]
    A -- "MCP / stdio" --> B
    B -- "HTTP + token<br/>(python-dotenv)" --> C
    C --> D
```

### Core Components

| File | Role |
|---|---|
| `src/second_brain_joplin/__init__.py` | Package metadata; exposes `__version__ = "0.1.0"`. Source: [src/second_brain_joplin/__init__.py:3](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/__init__.py) |
| `src/second_brain_joplin/server.py` | FastMCP server factory, argparse CLI (`serve` subcommand + `--version`), lazy `JoplinClient` singleton, and the five `@mcp.tool()` registrations. Source: [src/second_brain_joplin/server.py:16, 21-28, 34-58, 60-74](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py) |
| `src/second_brain_joplin/joplin_client.py` | Thin `httpx.AsyncClient`-based wrapper around the Joplin Data API. Only `ping()` is implemented; the rest raise `NotImplementedError`. Source: [src/second_brain_joplin/joplin_client.py:6-23](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/joplin_client.py) |
| `.env.example` | Documented template for `JOPLIN_API_TOKEN` and `JOPLIN_BASE_URL`. Source: [.env.example](https://github.com/jomkz/second-brain-joplin/blob/main/.env.example) |

The `_get_client()` helper in `server.py` instantiates `JoplinClient` on first use and caches it as a module-level singleton — it reads the token and base URL from the environment at that point. Source: [src/second_brain_joplin/server.py:19-28](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py).

### Registered MCP Tools (v0.1 stubs)

All five are `@mcp.tool()`-decorated `async` functions that short-circuit before reaching `JoplinClient`. Source: [src/second_brain_joplin/server.py:34-58](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py).

- `joplin_overview()` — list notebooks with note counts (issue #5).
- `joplin_search(query: str)` — keyword search (issue #6).
- `joplin_read(note_id: str)` — fetch a note's full body (issue #7).
- `joplin_recent(days: int = 7)` — recently modified notes (issue #8).
- `joplin_create(title: str, body: str, notebook_id: str)` — human-gated write, planned for v0.4 (issue #10).

## Configuration and Environment

Configuration is environment-only, loaded by `python-dotenv` at startup so a local `.env` "just works". Source: [src/second_brain_joplin/server.py:8, 65](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py).

| Variable | Default | Source |
|---|---|---|
| `JOPLIN_API_TOKEN` | *(none, required)* | [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md), [src/second_brain_joplin/server.py:22](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py) |
| `JOPLIN_BASE_URL` | `http://localhost:41184` | [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md), [src/second_brain_joplin/server.py:23](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py) |

Prerequisites are intentionally minimal: Joplin Desktop with the Web Clipper service enabled, the token copied from the Web Clipper settings, and either Python ≥ 3.11 or [`uvx`](https://docs.astral.sh/uv/) for zero-install execution. Source: [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md).

## Development Workflow and CI/CD

The toolchain is uv-managed and the source of truth for tooling versions is `.pre-commit-config.yaml`. Source: [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md).

- **Local loop** — `uv sync` → `uv run pre-commit install` → `uv run pytest`; ruff lint/format runs via `uvx pre-commit run --all-files`, mypy via `uv run mypy src`. Source: [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md).
- **Coverage gate** — 90% coverage is enforced in the CI command, not in `addopts`, so partial test runs do not falsely fail. Source: [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md), [CHANGELOG.md](https://github.com/jomkz/second-brain-joplin/blob/main/CHANGELOG.md).
- **Reusable checks workflow** — `.github/workflows/checks.yml` centralizes lint, type-check, the Python 3.11/3.12/3.13 test matrix, and a packaging build + `twine check` smoke test; both `ci.yml` (push/PR) and `release.yml` (publish) call it. Source: [CHANGELOG.md](https://github.com/jomkz/second-brain-joplin/blob/main/CHANGELOG.md), [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md).
- **Type checking** — mypy is a required gate (currently pragmatic; tightening to `strict` is tracked in issue #19). Source: [CHANGELOG.md](https://github.com/jomkz/second-brain-joplin/blob/main/CHANGELOG.md).

## Security and Privacy Posture

The server is local-only: it never sends notes to a third party, the Joplin API token is read from the environment, and writes are explicitly out of scope in v0.1. Source: [SECURITY.md](https://github.com/jomkz/second-brain-joplin/blob/main/SECURITY.md). Note creation is deliberately deferred to a human-gated flow in v0.4 so that "no writes happen without your confirmation". Source: [SECURITY.md](https://github.com/jomkz/second-brain-joplin/blob/main/SECURITY.md), [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md). The community already notes the need to enforce component-label automation via `actions/labeler` (issue #18) and to ship a v1.0 release pipeline (issue #12), both of which sit on the v1.0 milestone. Source: [community context, issues #11, #12, #18](https://github.com/jomkz/second-brain-joplin/issues).

## Roadmap and Status

| Milestone | Theme | Status |
|---|---|---|
| v0.1 — Bootstrap | Repo, CI, package skeleton, stubbed tools | Shipped |
| v0.2 — Core Read Tools | `joplin_overview`, `joplin_search`, `joplin_read`, `joplin_recent` implemented | Planned |
| v0.3 — Semantic Search | Embedding index + `joplin_semantic_search` (issue #9) | Planned |
| v0.4 — Write Workflow | Human-gated `joplin_create` (issue #10) | Planned |
| v1.0 — Publish | OIDC-trusted PyPI release, full docs, PARA templates (issues #11, #12) | Planned |

Source: [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md), [community context, issues #9, #10, #11](https://github.com/jomkz/second-brain-joplin/issues).

## See Also

- [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md) — quickstart, MCP tools table, and prerequisites.
- [CHANGELOG.md](https://github.com/jomkz/second-brain-joplin/blob/main/CHANGELOG.md) — full change history for the v0.1 release and unreleased CI/typing work.
- [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md) — local dev setup, CI mechanics, and label conventions.
- [SECURITY.md](https://github.com/jomkz/second-brain-joplin/blob/main/SECURITY.md) — vulnerability reporting and data-handling policy.
- [src/second_brain_joplin/server.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py) — FastMCP tool definitions and CLI entry point.
- [src/second_brain_joplin/joplin_client.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/joplin_client.py) — Joplin Data API wrapper used by the tools.
- [docs/install.md](https://github.com/jomkz/second-brain-joplin/blob/main/docs/install.md) — per-client MCP configuration guides.
- [docs/notebook-structure.md](https://github.com/jomkz/second-brain-joplin/blob/main/docs/notebook-structure.md) — recommended PARA notebook layout.
- [docs/project-management.md](https://github.com/jomkz/second-brain-joplin/blob/main/docs/project-management.md) — issue types, milestones, and label scheme.

---

<a id='page-tools'></a>

## MCP Tools Reference

### Related Pages

Related topics: [Project Overview and System Architecture](#page-overview), [Development Workflow, Testing, and CI/CD](#page-dev-cicd)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/second_brain_joplin/server.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py)
- [src/second_brain_joplin/joplin_client.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/joplin_client.py)
- [src/second_brain_joplin/__init__.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/__init__.py)
- [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md)
- [CHANGELOG.md](https://github.com/jomkz/second-brain-joplin/blob/main/CHANGELOG.md)
- [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md)
- [SECURITY.md](https://github.com/jomkz/second-brain-joplin/blob/main/SECURITY.md)
</details>

# MCP Tools Reference

## Overview

`second-brain-joplin` exposes a [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server named `"second-brain-joplin"` that lets any MCP-capable AI client — Claude Code, Cursor, and others — read and (eventually) write to a local Joplin knowledge base through a small set of typed tools. The server is a thin [FastMCP](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py) wrapper around Joplin's Web Clipper REST API on `localhost:41184` (`README.md`).

In the current **v0.1 (Bootstrap)** release, all five tools are registered but **stubbed** — they short-circuit with a `{"status": "not implemented"}` payload and never call out to the (also-stubbed) `JoplinClient` methods (`server.py`). The rationale is to ship a package skeleton that boots, registers tools, and exposes a CLI (`serve` subcommand + `--version`) while real implementations land across v0.2–v0.4 (`CHANGELOG.md`).

## Tool Registry

Five tools are defined via `@mcp.tool()` decorators in `src/second_brain_joplin/server.py` and listed in the README. The table maps each tool to its planned API call, the GitHub issue tracking its implementation, and whether it writes.

| Tool | Planned Action | Tracking Issue | Writes? |
|---|---|---|---|
| `joplin_overview` | `GET /notebooks` (recurse, build tree) | [#5](https://github.com/jomkz/second-brain-joplin/issues/5) | No |
| `joplin_search` | `GET /search?query=<q>` | [#6](https://github.com/jomkz/second-brain-joplin/issues/6) | No |
| `joplin_read` | `GET /notes/<id>?fields=id,title,body` | [#7](https://github.com/jomkz/second-brain-joplin/issues/7) | No |
| `joplin_recent` | `GET /notes?order_by=updated_time&order_dir=DESC` | [#8](https://github.com/jomkz/second-brain-joplin/issues/8) | No |
| `joplin_create` | Human-gated note creation | [#10](https://github.com/jomkz/second-brain-joplin/issues/10) | Yes (gated) |

Sources: [`server.py`](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py), [`README.md`](https://github.com/jomkz/second-brain-joplin/blob/main/README.md).

## Tool-by-Tool Reference

Each tool is an `async` function whose signature is part of the MCP contract that AI clients introspect. The signatures are stable even though the bodies are stubs (`server.py`).

### `joplin_overview`

```python
@mcp.tool()
async def joplin_overview() -> dict: ...
```

Returns a summary of the notebook tree — name, note count, and children — so the AI can orient before diving into individual notes (issue #5). No parameters.

### `joplin_search`

```python
@mcp.tool()
async def joplin_search(query: str) -> dict: ...
```

Keyword search across all notebooks via Joplin's `/search` endpoint. Issue #6 specifies the planned contract: required `query` plus an optional `limit` (default 10), returning matching `{id, title, excerpt}` records. This is the project's primary use case — letting the AI find notes by keyword (`README.md`, issue #6).

### `joplin_read`

```python
@mcp.tool()
async def joplin_read(note_id: str) -> dict: ...
```

Fetches the full markdown body of a single note. Issue #7 defines the response shape `{id, title, body}` and explicit handling for missing notes. Pairs naturally with `joplin_search`: the AI searches, picks an ID, then reads.

### `joplin_recent`

```python
@mcp.tool()
async def joplin_recent(days: int = 7) -> dict: ...
```

Lists notes modified within the last `days` days, defaulting to 7. Issue #8 frames this as "what have I been working on lately" context for the model — useful at the start of a session to ground the AI in recent work.

### `joplin_create`

```python
@mcp.tool()
async def joplin_create(title: str, body: str, notebook_id: str) -> dict: ...
```

The single write tool. Per issue #10, every call must pass through a human-in-the-loop confirmation (MCP prompt or similar) and requires the caller to specify the target notebook. Until v0.4 ships, this tool is also stubbed and never performs silent writes (`SECURITY.md`).

## Stub Payload Contract

Every stubbed tool returns the same shape today:

```json
{
  "status": "not implemented",
  "tool": "<tool name>",
  "issue": <tracking issue number>
}
```

This contract is defined in `server.py` (one entry per tool) and exists for two reasons: (1) clients receive a typed, parseable response rather than an exception, and (2) the embedded `issue` field points maintainers and downstream tooling at the tracking work item. The matching `JoplinClient` methods (`get_notebooks`, `get_note`, `search`, `get_recent`, `create_note`) raise `NotImplementedError` if reached — but in v0.1 they are unreachable because the tools short-circuit first (`joplin_client.py`).

## Architecture & Data Flow

The runtime architecture has three tiers: the MCP client (AI), the FastMCP server, and Joplin's local REST API. Configuration flows from environment variables (`JOPLIN_API_TOKEN`, `JOPLIN_BASE_URL`) loaded via `python-dotenv` (`server.py`).

```mermaid
flowchart LR
  Client["MCP client<br/>(Claude Code / Cursor)"] -->|MCP stdio| Server["second-brain-joplin<br/>FastMCP server"]
  Server -->|httpx + token| Joplin["Joplin Web Clipper API<br/>localhost:41184"]
  Joplin --> Notes[("Your Joplin notes")]
  Server -. stubbed .-> Server
```

Only `JoplinClient.ping()` is implemented end-to-end today — it returns `True` when the API responds with the literal string `JoplinClipperServer`, and `False` on a `httpx.ConnectError` (`joplin_client.py`). All other client methods are stubs pending the v0.2 milestone (`README.md`, `CHANGELOG.md`).

## Implementation Status & Roadmap

The tool layer is the focus of the next three milestones (`README.md`, `CHANGELOG.md`):

| Milestone | Theme | Tool Impact |
|---|---|---|
| [v0.2 — Core Read Tools](https://github.com/jomkz/second-brain-joplin/milestone/2) | Implement read tools | `joplin_overview`, `joplin_search`, `joplin_read`, `joplin_recent` become functional |
| [v0.3 — Semantic Search](https://github.com/jomkz/second-brain-joplin/milestone/3) | Embedding index (issue #9) | Adds `joplin_semantic_search` (bge-m3 via sentence-transformers, ChromaDB/FAISS) |
| [v0.4 — Write Workflow](https://github.com/jomkz/second-brain-joplin/milestone/4) | Human-gated creation | `joplin_create` ships behind a confirmation prompt |
| [v1.0 — Publish](https://github.com/jomkz/second-brain-joplin/milestone/5) | PyPI, full docs, PARA templates | Per-tool reference, configuration guide, troubleshooting (issue #11) |

Quality gates that protect the tool layer are run via the reusable `checks` workflow: ruff lint + format, mypy on `src`, pytest matrix on Python 3.11/3.12/3.13 with a 90% coverage gate, plus a packaging build/`twine check`/wheel smoke test (`CHANGELOG.md`, `CONTRIBUTING.md`).

## Security Notes

Tools inherit the privileges of the Joplin token they are configured with. Anything an MCP client can read, the connected AI can read — so users should apply least privilege and avoid notebooks containing credentials or regulated data (`SECURITY.md`). v0.1 performs no writes; v0.4 will require explicit human confirmation before `joplin_create` reaches the API.

## See Also

- [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md) — project overview, quickstart, comparison vs. `second-brain-mcp`
- [CHANGELOG.md](https://github.com/jomkz/second-brain-joplin/blob/main/CHANGELOG.md) — release notes for v0.1 and the `checks` workflow rework
- [SECURITY.md](https://github.com/jomkz/second-brain-joplin/blob/main/SECURITY.md) — vulnerability reporting and privacy model
- [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md) — dev setup, CI model, label conventions
- [v0.2 milestone](https://github.com/jomkz/second-brain-joplin/milestone/2) — tracking issue for read-tool implementations (#5–#8)
- [v0.4 milestone](https://github.com/jomkz/second-brain-joplin/milestone/4) — tracking issue #10 for the write tool

---

<a id='page-dev-cicd'></a>

## Development Workflow, Testing, and CI/CD

### Related Pages

Related topics: [MCP Tools Reference](#page-tools), [Installation, Deployment, and PyPI Release](#page-deploy-release)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md)
- [CHANGELOG.md](https://github.com/jomkz/second-brain-joplin/blob/main/CHANGELOG.md)
- [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md)
- [SECURITY.md](https://github.com/jomkz/second-brain-joplin/blob/main/SECURITY.md)
- [src/second_brain_joplin/__init__.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/__init__.py)
- [src/second_brain_joplin/server.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py)
- [src/second_brain_joplin/joplin_client.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/joplin_client.py)
- [pyproject.toml](https://github.com/jomkz/second-brain-joplin/blob/main/pyproject.toml)
- [.pre-commit-config.yaml](https://github.com/jomkz/second-brain-joplin/blob/main/.pre-commit-config.yaml)
- [.github/workflows/checks.yml](https://github.com/jomkz/second-brain-joplin/blob/main/.github/workflows/checks.yml)
- [docs/project-management.md](https://github.com/jomkz/second-brain-joplin/blob/main/docs/project-management.md)
</details>

# Development Workflow, Testing, and CI/CD

## Purpose and Scope

`second-brain-joplin` is a pre-alpha MCP server that wraps the Joplin Web Clipper REST API as MCP tools. Its development workflow must guarantee that any merged code can (a) start the FastMCP server, (b) pass lint, type-check, and a Python test matrix, and (c) build a publishable wheel/sdist — without leaking note data to third-party services. Source: [README.md:1-5](), [CHANGELOG.md:5-30](). The project is currently at v0.1.0, with a planned v0.2 covering real tool implementations, v0.3 for semantic search, v0.4 for human-gated writes, and v1.0 for the full PyPI release. Source: [src/second_brain_joplin/__init__.py:1-3](), [README.md:135-145]().

## Tooling: uv-First Bootstrap

The official toolchain is [uv](https://docs.astral.sh/uv/). New contributors run:

```bash
git clone https://github.com/jomkz/second-brain-joplin
cd second-brain-joplin
uv sync
uv run pre-commit install   # enable ruff lint/format on commit
```

A `pip` fallback is documented for users without uv: create a venv, activate it, and `pip install -e ".[test]"`. Source: [CONTRIBUTING.md:5-22](). The CI installs with `uv sync --locked` so a drift between `pyproject.toml` and `uv.lock` fails the build before tests even start. Source: [CHANGELOG.md:11-15]().

The server binary is a thin Python module: `__version__` is exported from `__init__.py`, and `server.py` exposes an `argparse` CLI whose only subcommand is `serve`, plus a `--version` flag. `load_dotenv()` is called in `main()` so a `.env` file (modeled on `.env.example`) is honored before any Joplin call. Source: [src/second_brain_joplin/server.py:30-65](), [README.md:60-78]().

## Testing Strategy

Tests live in `tests/` and run via:

```bash
uv run pytest
uv run pytest --cov-fail-under=90   # reproduce the CI gate locally
```

A **90% coverage gate** is enforced in CI but intentionally removed from `pytest` `addopts`, so running a subset of tests locally never spuriously fails. Source: [CONTRIBUTING.md:27-32](), [CHANGELOG.md:27-30](). The HTTP layer is tested with `respx`, which mocks `httpx.AsyncClient` calls; this is essential because `JoplinClient.ping()` performs an actual `await client.get(...)` against `localhost:41184`. Source: [src/second_brain_joplin/joplin_client.py:11-21](). The matrix covers Python 3.11, 3.12, and 3.13. Source: [CONTRIBUTING.md:50-55](), [CHANGELOG.md:70-75]().

Stubbed tools short-circuit with `{"status": "not implemented", "tool": ..., "issue": <n>}` payloads pointing to GitHub issues #5–#8 and #10, so tests can assert on the stub shape without exercising the unimplemented `JoplinClient` methods. Source: [src/second_brain_joplin/server.py:40-60](), [src/second_brain_joplin/joplin_client.py:23-39]().

## Static Analysis: Lint and Types

Lint is **driven by `pre-commit` in CI**, with `.pre-commit-config.yaml` as the single source of truth (no more version drift with the dev group). The hooks cover ruff `check` + `format --check` plus end-of-file, trailing-whitespace, and YAML/TOML hygiene. Source: [CHANGELOG.md:5-25](). The local command is `uvx pre-commit run --all-files`. Source: [CONTRIBUTING.md:36-41]().

`mypy` is a required CI gate (`uv run mypy src`) with a pragmatic baseline (`ignore_missing_imports`, `warn_unused_ignores`, `warn_redundant_casts`) because FastMCP's untyped `@mcp.tool()` decorator would fail under `strict = true`. Source: [CHANGELOG.md:17-22](), [pyproject.toml](). Issue #19 tracks gradual tightening to strict mode once those untyped decorators are accommodated. Source: [issue #19](https://github.com/jomkz/second-brain-joplin/issues/19).

## CI/CD Architecture

All quality gates live in one **reusable workflow** at `.github/workflows/checks.yml` — a `lint` job, a `typecheck` job, a `test` matrix job, and a `build` job that runs `uv build`, `twine check`, and a wheel smoke test. Both `ci.yml` (on push/PR to `main`) and `release.yml` (before publishing to PyPI) call it, so nothing merges or ships without passing the same checks. Source: [CONTRIBUTING.md:46-55](), [CHANGELOG.md:5-15](). The release job publishes the exact artifact built and tested in `checks` (build-once/test/publish) with `skip-existing` on re-runs. Source: [CHANGELOG.md:7-11]().

Least-privilege `permissions` and `concurrency` cancellation are configured to cancel in-flight runs on the same ref when a new commit is pushed. Source: [CHANGELOG.md:13-17](). PyPI publishing uses an OIDC trusted publisher; the release flow is tracked in issue #12 and milestones toward v1.0 in issue #11. Source: [issue #12](https://github.com/jomkz/second-brain-joplin/issues/12), [issue #11](https://github.com/jomkz/second-brain-joplin/issues/11). Maintainers also want an `actions/labeler` workflow to apply `component:*` labels automatically (issue #18). Source: [issue #18](https://github.com/jomkz/second-brain-joplin/issues/18).

```mermaid
flowchart LR
    A[push / PR to main] --> B[ci.yml]
    T[tag release] --> R[release.yml]
    B --> C[checks.yml]
    R --> C
    C --> L1[lint: pre-commit]
    C --> L2[typecheck: mypy src]
    C --> L3[test matrix: py 3.11/3.12/3.13 + 90% cov]
    C --> L4[build: uv build + twine check + smoke]
    L4 --> P[publish to PyPI via OIDC]
```

## Local Reproduction and Best Practices

To match CI exactly, run `uv sync --locked` (not plain `uv sync`) so your environment cannot silently drift from `uv.lock`. Source: [CHANGELOG.md:11-13](). Before opening a PR, execute the same sequence CI runs:

```bash
uvx pre-commit run --all-files
uv run mypy src
uv run pytest --cov-fail-under=90
```

Source: [CONTRIBUTING.md:36-41](). PRs must target `main`, stay focused on one logical change, include tests, and pass the full CI. Source: [CONTRIBUTING.md:84-90](). Maintainers triage issues by issue type, milestone, `component:*` labels, optional `phase-N` labels, and parent epics — see the label table in `CONTRIBUTING.md` and the model in `docs/project-management.md`. Source: [CONTRIBUTING.md:64-83](), [docs/project-management.md]().

A common failure mode is the **stubbed-tool surprise**: when testing without a running Joplin instance, every tool returns a `not implemented` payload regardless of arguments. This is by design for v0.1 — implementers should look at issues #5–#8 and #10 and use `respx` mocks for real HTTP coverage. Source: [src/second_brain_joplin/server.py:30-60](), [src/second_brain_joplin/joplin_client.py:23-39](). Another common failure is exposing `JOPLIN_API_TOKEN` in version control; the env var is read at runtime and never committed. Source: [SECURITY.md:12-18]().

## See Also

- `README.md` — Quickstart and per-client install guides
- `CHANGELOG.md` — Detailed release history
- `SECURITY.md` — Threat model and reporting guidelines
- `docs/install.md` — Per-MCP-client setup walkthroughs
- [Roadmap and milestone tracking on GitHub](https://github.com/jomkz/second-brain-joplin/milestones)

---

<a id='page-deploy-release'></a>

## Installation, Deployment, and PyPI Release

### Related Pages

Related topics: [Project Overview and System Architecture](#page-overview), [Development Workflow, Testing, and CI/CD](#page-dev-cicd)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/jomkz/second-brain-joplin/blob/main/README.md)
- [CHANGELOG.md](https://github.com/jomkz/second-brain-joplin/blob/main/CHANGELOG.md)
- [CONTRIBUTING.md](https://github.com/jomkz/second-brain-joplin/blob/main/CONTRIBUTING.md)
- [SECURITY.md](https://github.com/jomkz/second-brain-joplin/blob/main/SECURITY.md)
- [src/second_brain_joplin/server.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/server.py)
- [src/second_brain_joplin/joplin_client.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/joplin_client.py)
- [src/second_brain_joplin/__init__.py](https://github.com/jomkz/second-brain-joplin/blob/main/src/second_brain_joplin/__init__.py)
</details>

# Installation, Deployment, and PyPI Release

## Overview

`second-brain-joplin` is a Python MCP server (`__version__ = "0.1.0"`) that exposes a local Joplin knowledge base to any MCP-capable AI client such as Claude Code or Cursor. The server is distributed on PyPI and run via the [`uvx`](https://docs.astral.sh/uv/) launcher; it keeps notes local by speaking only to Joplin's Web Clipper REST API on `localhost:41184`. The v1.0 publish milestone and the PyPI trusted-publisher work are tracked in [issue #11](https://github.com/jomkz/second-brain-joplin/issues/11) and [issue #12](https://github.com/jomkz/second-brain-joplin/issues/12) respectively.

```mermaid
sequenceDiagram
    participant Client as MCP Client<br>(Claude Code / Cursor)
    participant Srv as second-brain-joplin serve
    participant Jop as Joplin Desktop<br>localhost:41184
    participant Notes as Joplin Notes

    Client->>Srv: stdio / MCP tool call
    Srv->>Jop: HTTP GET (X = token)
    Jop-->>Srv: JSON
    Srv-->>Client: tool result
    Jop->>Notes: local read/write
```

## Installation

### Prerequisites

- **Joplin Desktop** running with the Web Clipper service enabled (toggle in *Tools → Options → Web Clipper → Enable Web Clipper Service*).
- **API token** copied from that same Web Clipper settings panel.
- **Python ≥ 3.11** *or* the `uvx` runner (recommended — no global install needed).

Source: [README.md:31-38](README.md)

### Recommended path: `uvx`

For Claude Code, the README documents a single registration command that injects the API token through the environment and runs the server in-process:

```bash
claude mcp add -s user second-brain-joplin \
  -e JOPLIN_API_TOKEN=your-token-here \
  -- uvx second-brain-joplin serve
```

For other MCP clients, three settings are sufficient — the `serve` subcommand, `JOPLIN_API_TOKEN` from Joplin Web Clipper, and `JOPLIN_BASE_URL` defaulting to `http://localhost:41184`. Source: [README.md:42-53](README.md)

### Alternative path: `pip` (development)

Contributors use the bundled `uv` toolchain:

```bash
git clone https://github.com/jomkz/second-brain-joplin
cd second-brain-joplin
uv sync
uv run pre-commit install
```

A pip fallback (`python -m venv .venv && pip install -e ".[test]"`) is documented for environments without `uv`. Source: [CONTRIBUTING.md:9-22](CONTRIBUTING.md)

## Configuration and Local Run

The CLI surface is `second-brain-joplin serve` plus a `--version` flag. At startup, `main()` calls `load_dotenv()` *before* argument parsing, so a local `.env` file is picked up automatically; the recommended flow is `cp .env.example .env`, edit, then `uv run second-brain-joplin serve`. Source: [src/second_brain_joplin/server.py:64-71](src/second_brain_joplin/server.py); [CONTRIBUTING.md:30-33](CONTRIBUTING.md)

`_get_client()` lazily constructs the singleton `JoplinClient`, reading `JOPLIN_API_TOKEN` (defaulting to `""`) and `JOPLIN_BASE_URL` (defaulting to `http://localhost:41184`). The `JoplinClient` constructor strips a trailing slash from the base URL and stores the token in a reusable query-params dict used for every request. Source: [src/second_brain_joplin/server.py:12-21](src/second_brain_joplin/server.py); [src/second_brain_joplin/joplin_client.py:11-15](src/second_brain_joplin/joplin_client.py)

## CI/CD and the PyPI Release Path

Quality gates and the release pipeline share a single reusable workflow, [`.github/workflows/checks.yml`](.github/workflows/checks.yml), invoked by both [`ci.yml`](.github/workflows/ci.yml) (push/PR) and [`release.yml`](.github/workflows/release.yml) (pre-publish). Releases therefore cannot ship code that has not passed the full check suite, and the release job follows a build-once/test/publish pattern — re-using the exact artifact built and tested in `checks`, with `skip-existing` on re-runs. Source: [CHANGELOG.md:7-21](CHANGELOG.md); [CONTRIBUTING.md:23-31](CONTRIBUTING.md)

The `checks` workflow runs:

- `pre-commit run --all-files` — single source of truth for ruff lint + format (CI invokes the *exact* same command contributors run locally).
- `uv run mypy src` — static type checking, now a required CI gate.
- `pytest` on a **Python 3.11 / 3.12 / 3.13** matrix with a **90% coverage gate**.
- A **build job** that runs `uv build`, `twine check`, and a wheel smoke test.

Source: [CHANGELOG.md:9-33](CHANGELOG.md); [CHANGELOG.md:46-50](CHANGELOG.md)

CI also installs with `uv sync --locked` (a drifted `uv.lock` fails the run), adds least-privilege `permissions` and `concurrency` cancellation, and validates packaging on every PR. Source: [CHANGELOG.md:11-21](CHANGELOG.md)

Publication to PyPI uses an [OIDC trusted publisher](https://docs.pypi.org/trusted-publishers/) — no long-lived PyPI token is stored in the repo, and the project is already registered as a trusted publisher on pypi.org. Source: [issue #12](https://github.com/jomkz/second-brain-joplin/issues/12)

### Reproducing the gates locally

```bash
uv run pytest --cov-fail-under=90
uvx pre-commit run --all-files
uv run mypy src
uv build && twine check dist/*
```

Note that `--cov-fail-under=90` was moved out of pytest `addopts` into the CI command specifically so partial local runs don't spuriously fail the gate. Source: [CHANGELOG.md:30-33](CHANGELOG.md); [CONTRIBUTING.md:18-31](CONTRIBUTING.md)

## Failure Modes and Operational Notes

- **Missing token** — `JoplinClient` is constructed with `token=""` if `JOPLIN_API_TOKEN` is unset; `ping()` will fail at runtime rather than at import.
- **Joplin not running** — `ping()` catches `httpx.ConnectError` and returns `False`, so the MCP server stays up but downstream data calls surface the connection error.
- **Drifted lockfile** — `uv sync --locked` is a hard gate.
- **Coverage gate** — enforced only on the full CI command, not on `pytest` by default.

Source: [src/second_brain_joplin/server.py:12-21](src/second_brain_joplin/server.py); [src/second_brain_joplin/joplin_client.py:17-23](src/second_brain_joplin/joplin_client.py)

## Security Considerations

The server runs locally, reads the Joplin token only from the environment, and never commits it. v0.1 tools are read-oriented and stubbed; planned `joplin_create` writes will be explicitly human-gated before any silent write occurs. Apply least-privilege to notebooks containing credentials or regulated data — anything an MCP client can read, the connected AI can read. Source: [SECURITY.md:11-21](SECURITY.md); [README.md:67-74](README.md)

## See Also

- Tool reference — `joplin_overview`, `joplin_search`, `joplin_read`, `joplin_recent`, `joplin_create`
- v0.2 implementation work — [milestone: Core Read Tools](https://github.com/jomkz/second-brain-joplin/milestone/2)
- v1.0 publish epic — [issue #11](https://github.com/jomkz/second-brain-joplin/issues/11) (full docs, PARA templates, `uvx` smoke) and [issue #12](https://github.com/jomkz/second-brain-joplin/issues/12) (PyPI OIDC publish)

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: jomkz/second-brain-joplin

Summary: Found 32 structured pitfall item(s), including 2 high/blocking item(s). Top priority: Security or permission risk - Security or permission risk requires verification.

## 1. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Developers should check this security_permissions risk before relying on the project: Add PR labeler workflow (actions/labeler)
- User impact: Developers may expose sensitive permissions or credentials: Add PR labeler workflow (actions/labeler)
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/18

## 2. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Developers should check this security_permissions risk before relying on the project: Set up CI pipeline (lint + test matrix)
- User impact: Developers may expose sensitive permissions or credentials: Set up CI pipeline (lint + test matrix)
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/2

## 3. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: Bootstrap second-brain-joplin (v0.1)
- User impact: Developers may fail before the first successful local run: Bootstrap second-brain-joplin (v0.1)
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/1

## 4. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/1

## 5. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/9

## 6. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.host_targets | https://github.com/jomkz/second-brain-joplin

## 7. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: Implement core read MCP tools (v0.2)
- User impact: Developers may misconfigure credentials, environment, or host setup: Implement core read MCP tools (v0.2)
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/4

## 8. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: Implement package skeleton and stubbed MCP server
- User impact: Developers may misconfigure credentials, environment, or host setup: Implement package skeleton and stubbed MCP server
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/3

## 9. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: PyPI publish, full docs, and Joplin templates (v1.0)
- User impact: Developers may misconfigure credentials, environment, or host setup: PyPI publish, full docs, and Joplin templates (v1.0)
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/11

## 10. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/19

## 11. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/jomkz/second-brain-joplin

## 12. Runtime risk - Runtime risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a runtime risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/7

## 13. Runtime risk - Runtime risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a runtime risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/6

## 14. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/jomkz/second-brain-joplin

## 15. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/jomkz/second-brain-joplin

## 16. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/jomkz/second-brain-joplin

## 17. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/18

## 18. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/4

## 19. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/5

## 20. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/3

## 21. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/12

## 22. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/jomkz/second-brain-joplin/issues/2

## 23. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this capability risk before relying on the project: Human-gated note creation (v0.4)
- User impact: Developers may hit a documented source-backed failure mode: Human-gated note creation (v0.4)
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/10

## 24. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this capability risk before relying on the project: Implement joplin_overview tool
- User impact: Developers may hit a documented source-backed failure mode: Implement joplin_overview tool
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/5

## 25. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this capability risk before relying on the project: Implement joplin_read tool
- User impact: Developers may hit a documented source-backed failure mode: Implement joplin_read tool
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/7

## 26. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this capability risk before relying on the project: Implement joplin_recent tool
- User impact: Developers may hit a documented source-backed failure mode: Implement joplin_recent tool
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/8

## 27. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this capability risk before relying on the project: Implement joplin_search tool
- User impact: Developers may hit a documented source-backed failure mode: Implement joplin_search tool
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/6

## 28. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this capability risk before relying on the project: Semantic search via embedding index (v0.3)
- User impact: Developers may hit a documented source-backed failure mode: Semantic search via embedding index (v0.3)
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/9

## 29. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this capability risk before relying on the project: Tighten mypy toward strict and cover tests/
- User impact: Developers may hit a documented source-backed failure mode: Tighten mypy toward strict and cover tests/
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/19

## 30. Capability evidence risk - Capability evidence risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: Developers should check this conceptual risk before relying on the project: Publish package to PyPI (trusted publisher)
- User impact: Developers may hit a documented source-backed failure mode: Publish package to PyPI (trusted publisher)
- Evidence: failure_mode_cluster:github_issue | https://github.com/jomkz/second-brain-joplin/issues/12

## 31. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/jomkz/second-brain-joplin

## 32. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/jomkz/second-brain-joplin

<!-- canonical_name: jomkz/second-brain-joplin; human_manual_source: deepwiki_human_wiki -->
