jotdownload

blog · · Mohak Bajaj

My notes app has no database, just a folder of Markdown and an MCP server

I make a small Mac app called Jot. You select text anywhere, tap Shift twice, and it saves a note. When I started, I thought about SQLite, a sync server and an account system. I shipped none of them. All of Jot's storage is a folder: ~/Documents/Jot.

This post covers how that folder works, what broke, and why AI agents turned out to be the best reason for it.

One note is one file

Every note is a Markdown file with YAML front matter:

~/Documents/Jot/
  2026-11-09-6tjhzv.md
  2026-11-09-gnump0.md
  INDEX.md
  SKILL.md
  attachments/6tjhzv/
  .trash/
---
archived: false
created: 2026-11-09T09:12:04.511Z
done: false
id: 6tjhzv
images: []
pin: false
section: ''
source: Safari
url: https://example.com/article
tags: [reading]
updated: 2026-11-09T09:12:04.511Z
---
A line I selected and wanted to keep.

The filename is YYYY-MM-DD-<id>.md. The id is 6 random characters from [0-9a-z]. The date at the front makes ls sort by day for free. If a file loses its created field, Jot falls back to the date in the name, so the order stays stable.

Images and audio go in attachments/<id>/. A deleted note moves to .trash/<id>/, so undo is a rename back.

The problems a database would have solved

Half-written files. If the app crashes in the middle of a write, the note is gone. So every write goes to a temp file next to the real one (.name.<16 hex>.tmp), and then a rename puts it in place. A rename is atomic on one filesystem. The rename lives in the folder entry, so Jot also syncs the folder, or a power cut can undo it. When Jot opens the vault, it deletes temp files left by a crash.

Reading every file, every time. Each operation lists the folder again. Parsing hundreds of files with YAML on each keypress is slow. Jot keeps a cache keyed by inode, mtime and size. An atomic write gives the file a new inode, so any change misses the cache, and a file that did not change is not parsed again.

A file someone else broke. You can edit these files in Vim, Obsidian or a script. If the front matter stops parsing, Jot leaves that one file out of the list, logs why once, and keeps going. It does not crash, and it does not "fix" your file.

Changes from outside. Jot watches the folder. If another program adds or edits a file, the inbox updates by itself.

That is the whole storage layer, in about 600 lines of TypeScript.

Then agents showed up

I added a CLI first (jot list, jot search, jot add, jot tag, jot done, jot archive). Then jot mcp started an MCP server on stdio, so Claude Code, Codex and Cursor can use the same vault.

The MCP server has seven tools:

ToolWhat it does
list_jotsList by status, section or tag, newest first
search_jotsCase-insensitive match on body, tags, source app and URL
read_jotThe whole file, plus absolute paths to its images
add_jotCreate a note; the first line is its title
tag_jotAdd tags; existing tags stay
set_doneMark done, or open again
archive_jotHide from the inbox; the file stays

There is no delete tool, on purpose. An agent can add, tag, finish and archive. It cannot remove anything. The worst an agent can do is leave a mess you can see in Finder.

The server also does very little. It reads the files, changes the front matter, and writes them back with the same atomic write. The app sees the change through the folder watcher. There is no IPC between the server and the app, and the app does not need to be running.

Files help agents even without MCP

Two generated files are for agents:

So the setup for most agents is just this: "my notes are in ~/Documents/Jot, read SKILL.md." Something like "what was I thinking about last week" becomes a grep and a read.

What I gave up

For a capture inbox, these trades were worth it. Your notes outlive the app, every tool you already use can open them, and ls ~/Documents/Jot is the admin panel.


Jot is a Mac app for capture and dictation. It's free for 3 days, then $9 once at the launch price. sayjot.xyz