NoteLiner is a single-user outliner-style note-taking application, built with Electron and Svelte 5. It allows users to create and organize files in a hierarchical structure. Each file is written in Markdown with syntax highlighting, and all changes are automatically synced via Git.
NoteLiner should be considered BETA software. I wrote it for my own personal use, and it meets my specific needs.
NoteLiner works with the concept of a Project, which is a folder on the user's computer containing:
noteliner.json) tracking all files, their hierarchy, tags, and attachments..md) in the project root._attachments/ directory for files attached to notes (images, PDFs, etc.).Changes are committed locally whenever a file is added, removed, or modified. Commits are pushed to the remote repository on a 30-second debounce timer, enabling cross-device sync via Git.
A vertical toolbar with icon buttons:
| Button | Shortcut | Description |
|---|---|---|
| Open Folder | Ctrl+O |
Open a project folder. Loads existing projects or prompts to create/clone. |
| New File | Ctrl+N |
Create a new file in the project. |
| Attachments | Ctrl+B |
Toggle the attachments sidebar. |
| Show Log | Ctrl+L |
Toggle the git sync log panel. |
| Project Settings | Ctrl+Shift+, |
Configure git user name/email, view project location. |
| Settings | Ctrl+, |
Change application theme. |
| About | Ctrl+I |
Show application name and version. |
Ctrl+P) -- Live rendered markdown preview with image support.Ctrl+B) -- Right panel showing files attached to the current note. Images display as thumbnails; other files show a type icon. Add via paste, drag-and-drop, or the [+] button (native file picker). Double-click to open with the system default application.Ctrl+L) -- Bottom panel showing git sync activity in real time.All modal dialogs support Enter to confirm and Escape to close.
Files can be attached to any note via:
[+] in the attachments panel.Attachments are stored in the _attachments/ directory and tracked in noteliner.json. A markdown reference is automatically inserted at the cursor:  for images, [name](path) for other files. Maximum file size is 30MB.
NoteLiner can expose the currently-open project to external AI assistants (Claude Code, Claude Desktop, Cursor, ...) via the Model Context Protocol. When enabled, the AI can list, read, search, and write notes — every write goes through the same save path as the UI and is committed to git automatically.
The server is local-only: no TCP port is opened. NoteLiner listens on a
Unix domain socket (or Windows named pipe) that only the local user can
read, and external clients connect through a small noteliner-mcp-bridge
helper that pipes stdio to that socket.
Settings → MCP → toggle "Enable MCP server". Every off→on transition opens a one-screen walkthrough that explains the architecture and offers a copy-paste config snippet for your client. The server only runs while a project is open; closing the project (or disabling MCP) tears it down.
Drop this into your MCP client's config file — for Claude Code, that's
.mcp.json in the project root; for Claude Desktop,
claude_desktop_config.json:
{
"mcpServers": {
"noteliner": {
"command": "node",
"args": ["/absolute/path/to/noteliner-mcp-bridge.js"]
}
}
}
The exact bridge path for your install appears in Settings → MCP → Client Configuration with a one-click copy button. Don't hand-edit the path — it differs between dev runs and packaged installs.
| Tool | Purpose | Kind |
|---|---|---|
list_notes |
List every note (id, name, filename, tags, parentId) | read |
read_note |
Read a note's body by id or name | read |
search |
Full-text search across note bodies | read |
get_backlinks |
List notes linking to a target via wikilinks | read |
list_attachments |
List attachments on a note | read |
list_tags |
Tag histogram with note counts and ids | read |
create_note |
Create a new note | write |
update_note |
Replace a note's body | write |
delete_note |
Delete a note (children reparent) | write |
rename_note |
Rename a note (filename re-slugged) | write |
set_tags |
Replace a note's tag list | write |
add_attachment |
Attach a binary file (base64, 30MB cap) | write |
remove_attachment |
Remove an attachment by id | write |
Three resource URIs are also exposed for clients that prefer
resource-style reads: noteliner://index (the full noteliner.json),
noteliner://note/{id} (a note's body), and
noteliner://attachment/{filename} (binary attachment data, base64).
Four MCP prompts surface in clients that render prompts as slash
commands: daily_note (with optional date/topic), meeting_note
(required topic + optional attendees), summarize_note (server-side
fetches the body and embeds it), and link_suggestions (server-side
includes the body plus the names of all other notes so the model can
suggest wikilink targets).
chmod 0600; Windows
named pipes are user-scoped by default. No TCP port is bound, no
remote transport is supported.ProjectService.Every MCP tool call is logged to the Log panel (Ctrl+L), prefixed
[MCP], so you can see exactly what the AI just did.
Released builds display the bare semver from package.json (e.g. 0.5.0).
Local development builds append the short git commit hash and a dev marker
(e.g. 0.5.0-dev.8c6e3d6) so it's always obvious whether you're running an
unreleased build. The version is injected at build time via Vite's define.
Pre-built binaries for Linux, Windows, and macOS are published from the Releases page on each tag.
.AppImage, chmod +x, run. Or
sudo dpkg -i noteliner_*_amd64.deb..exe installer, run. The first launch may
show a SmartScreen warning (current builds are unsigned); click
"More info" → "Run anyway.".dmg, drag NoteLiner to /Applications. The
first launch will be blocked by Gatekeeper (current builds are unsigned);
right-click NoteLiner in /Applications → "Open" → "Open" to bypass.Once a release is installed, NoteLiner checks for updates on each launch and downloads them in the background. The Log panel shows progress.
npm install
npm run electron:dev # Start in development mode
npm run build # Build the renderer (Vite)
npm run start # Run the built Electron app
npm run build:linux # AppImage + .deb (Linux only)
npm run build:win # NSIS installer + portable .exe
npm run build:mac # .dmg + .zip (universal)
npm run build:all # all three (cross-compile; works best on macOS)
Output lands in dist-electron/. Cross-compilation has limits — the
authoritative builds happen in CI via .github/workflows/release.yml on
tag push, with one runner per OS.
The 512×512 build icon (build/icon.png) is checked in; regenerate it
from assets/icon.png with npm run build:icons if the source icon
changes (requires ImageMagick).
Two suites cover different layers. Both run from the repo root.
Five focused tests that boot the real Electron app and assert against
on-disk state: app launches, create-and-persist, rename, delete, and
attachments. Run these whenever you touch the renderer, the IPC layer,
or ProjectService.
npm run build # tests load dist/, so build first
npm test # headless
npm run test:headed # show the Electron window
npm run test:debug # step through with the Playwright Inspector
The first run installs Playwright's bundled browsers automatically.
See tests/README.md for the test mandate and how
the Electron harness works.
Spawns bin/noteliner-mcp-bridge.js as a child process and drives it
with a synthetic JSON-RPC client — the same wire path Claude Code,
Claude Desktop, and Cursor use. Twenty-four assertions across the six
items from the MCP plan's Testing section: list/create round-trip,
search parity with the in-app search, mid-call disconnect, project
switch isolation, and stale-socket recovery.
npm run test:mcp # ~3 seconds; requires git on PATH
Runs in plain Node — no Electron, no browser. Suitable for CI as well
as a fast pre-commit check whenever you touch src/main/mcp-service.js
or the bridge binary.
See tests/integration/README.md for
coverage details and the short manual checklist for UI-side
verification with a real MCP client (the bits that genuinely need a
human eyeballing the FILES pane).