Capture MCP Server
Uses an OpenAI-compatible chat-completions endpoint for automatic tagging of new captures: the capture text, configured tags and tags already in use are sent to the model, which returns a single tag (preferring existing tags, or coining a short general one). Failures fall back to the 'inbox' tag and never block saving.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Capture MCP Servercapture this: buy milk and eggs on the way home"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Capture
Capture anything the moment it occurs to you.
Capture is a fast, local-first web app for catching thoughts, ideas and tasks the instant they happen. One keystroke drops you into a focused capture overlay; an LLM auto-tags what you captured; a calm, responsive UI keeps everything organised by tag. An MCP endpoint lets agents add, search, edit, archive and delete captures without ever touching the UI.
Everything is stored in a local SQLite database — no third-party services, no account, no network beyond your LLM endpoint.
Features
One-keystroke capture — press
N(or click New) and a blurred overlay takes over. Enter adds a new line;Ctrl/⌘+Enter(or the Save button) captures. Works on desktop and mobile.LLM auto-tagging — new captures are tagged by any OpenAI-compatible chat-completions endpoint. The LLM has free rein: it prefers the default tags and any tag already in use, but may coin a new general tag (one or two words, never a sentence) when nothing fits. Tagging can be disabled; the capture is never blocked by the LLM (failures fall back to
inbox).Three views — a board grouped by tag (for the visual), a chronological list with a tag filter (for the data-driven), and a force-directed graph that links each capture to the tag hubs it carries — click a tag to isolate its cluster and see where captures interconnect. Live search across content and tags in all three.
Edit, archive, delete — editing reuses the capture overlay with an "Editing capture" context; archiving is instant with an Undo toast; deleting asks for confirmation first.
MCP server — the six core functions are exposed over streamable HTTP at
/mcp, so any MCP-capable agent can work with your captures.Runs as a service —
capture installinstalls it as a background service on Linux (systemd), macOS (launchd) and Windows (Task Scheduler), with auto-restart on Linux and macOS.Single-file configuration — everything lives in
settings.yaml, including the tag list, which is read from disk as needed: add a tag in the file and it appears in the UI, the LLM prompt and the MCP tools immediately.
Related MCP server: mcp-notes-server
Quick start
Requirements: Python 3.10+.
git clone <your-fork-of-this-repo> capture
cd capture
python -m venv .venv
source .venv/bin/activate
pip install -e .
cp example.settings.yaml settings.yaml # copy the example config
capture # or: python -m captureThen open http://localhost:8000.
In the background:
capture installsets up a service that starts at boot/login and keeps the app running — Running as a background service.From your phone: the app binds to
0.0.0.0by default, so on the same Wi-Fi openhttp://<your-machine's-ip>:8000. Setserver.host: 127.0.0.1insettings.yamlto keep it local-only.CLI overrides:
capture --host 127.0.0.1 --port 9000 --settings /path/to/settings.yaml.
The first run creates the SQLite database (data/captures.db by default).
Running as a background service
capture install installs the app as a service that starts at boot/login and keeps
running in the background. capture uninstall stops and removes it again.
Platform | What you get |
Linux (systemd) — Ubuntu, Fedora, RHEL, Arch, openSUSE, NixOS, … | a systemd user service by default (no root needed); |
macOS | a launchd LaunchAgent (per-user, starts at login, auto-restarts) |
Windows | a Task Scheduler task that runs the app at logon |
capture install # per-user service (default; host/port from settings.yaml)
capture install --port 9000 # override the port (--host / --settings work too)
capture install --system # Linux only: system-wide service (needs root)
capture uninstall # stop and remove the serviceHow it works:
The unit is generated with absolute paths (the interpreter you ran from, the project root,
settings.yaml), so the service behaves exactly like the CLI. Keep the repo and its venv where they are — the unit points at them.Linux units restart the app automatically on crash (
Restart=on-failure); the macOS LaunchAgent does the same viaKeepAlive. The Windows scheduled task re-runs the app at each logon but does not restart crashes — a true Windows service needs a wrapper such as NSSM, which this tool deliberately avoids as a dependency.capture installrefuses to start if the port is already in use, so you can't end up with two instances fighting over it.
Logs and status:
Platform | Status | Logs |
Linux (user) |
|
|
Linux (system) |
|
|
macOS |
|
|
Windows |
| run the app in the foreground to see console output |
Notes:
Linux: the user service runs inside your login session. To keep it running after you log out, enable lingering with
sudo loginctl enable-linger <your-user>— the installer prints this hint when it applies.macOS: the agent lives at
~/Library/LaunchAgents/com.capture.app.plist; log files in~/Library/Logs/capture/are kept after uninstall.Windows: the task is created for the current user; no password is stored.
Using the web UI
Action | How |
New capture |
|
Add a line |
|
Save capture |
|
Cancel |
|
Search |
|
Switch view | Board (grouped by tag) / List (chronological + tag filter) / Graph (tag-connection map) |
Edit | pencil icon on a card — reuses the overlay, pre-filled, shows "Editing capture" |
Archive | archive icon — instant, no confirmation, toast offers Undo for a few seconds |
Restore | from the Archived section (board) or the divider section (list) |
Delete | trash icon — a confirmation dialog must be accepted first |
Graph: cards | captures render as small cards (tag hubs stay circular); zoom in and each card shows a short truncated snippet of its content |
Graph: hover | hover a node — captures show content, id, time and tags (plus a double-click to edit hint); tags show their capture count |
Graph: focus | click a tag hub to isolate its cluster and dim the rest; click empty space to clear |
Graph: zoom to a card | click a capture to zoom in on it while keeping its neighbours in view; double-click a capture to open the edit overlay |
Graph: pan/zoom | drag empty space to pan, scroll or drag the right-side slider to zoom (0.2×–8×), drag a node to reposition it |
Tags are multi-valued: a capture can carry several, and in the board view it appears under each. In the capture overlay you can pick tags manually — the picker lists the configured tags plus any the LLM has already coined. If you leave tags blank (on a new capture only) the LLM assigns one.
Configuration
All settings live in a single settings.yaml at the project root. The file
is gitignored — your personal settings (host/port, LLM endpoint, tag list)
stay out of the repo. On a fresh clone, create it from the bundled example
(cp example.settings.yaml settings.yaml), then edit to taste:
server:
host: 0.0.0.0 # bind address (127.0.0.1 = local-only)
port: 8000
database:
path: data/captures.db # relative to the project root
llm:
enabled: yes # no = skip the LLM, tag everything inbox
url: http://localhost:8080/v1 # any OpenAI-compatible base URL
model: qwen3.8-27b # model name served at that URL
tags:
- inbox
- todo
- idea
- urgentKey | Meaning |
| Where the web app (and MCP endpoint) listen. Overridable with |
| SQLite file location, relative to the project root. |
|
|
| Base URL of an OpenAI-compatible chat-completions endpoint (a |
| Model identifier. |
| The default tag list. The LLM prefers these (and any tag already in use) but may coin a new general tag; the UI and MCP/API accept this list plus every tag already in use. Re-read from the file as needed — editing it requires no restart. |
LLM auto-tagging
When a capture is added without explicit tags, Capture calls
POST {llm.url}/chat/completions with a classifier prompt. The model sees the
capture text, the default tag list, and the tags already in use across your
captures — and must answer with a JSON object {"tag": "<tag>"}.
The LLM has free rein over the vocabulary:
It prefers existing tags (the defaults, then anything already in use) so related captures share tags and stay consistent over time.
It may coin a new tag when nothing fits, but is told to keep it general — one or two words, never a sentence or specific detail. The parser enforces this: a new tag that isn't one or two short words is rejected and the capture falls back to
inbox.inboxis for text that genuinely resists any tag.
Guarantees:
The capture is saved no matter what. If the LLM is down, times out (30 s), returns an unusable tag, or returns nothing parseable, the capture is tagged
inbox(or the first configured tag ifinboxisn't in the list). A failed LLM call is logged, never fatal.Reasoning models are supported. The completion budget is generous (512 tokens) so models that "think" before answering still fit their answer; the parser takes the JSON object (with a bare-word fallback).
Manual tags always win. If you (or an agent) supply tags, the LLM is not consulted.
The context is always current — the prompt is built from a fresh read of
settings.yamland a fresh query of the tags in use, on every capture.
MCP
The app serves a streamable-HTTP MCP endpoint at:
http://<host>:<port>/mcp(point at the machine:port from settings.yaml — e.g.
http://localhost:8000/mcp, or your LAN IP for agents on other devices).
Add it to any MCP-capable client. Generic config:
{
"mcpServers": {
"capture": {
"url": "http://localhost:8000/mcp"
}
}
}Tools
Tool | Purpose |
| All captures, newest first. |
|
|
|
|
|
|
|
|
|
|
Example session:
> list_captures()
[{"id": 3, "content": "Idea: ...", "tags": ["idea"], "archived": false, ...}, ...]
> add_capture(content="Call the dentist tomorrow")
{"id": 4, "content": "Call the dentist tomorrow", "tags": ["todo"], ...}
> archive_capture(id=4)
{"id": 4, ..., "archived": true, ...}REST API
The web UI is a thin client of this API — useful for scripting too.
Method & path | Description |
|
|
|
|
|
|
| One capture, or |
| Any of |
|
|
curl -s localhost:8000/api/captures -H 'Content-Type: application/json' \
-d '{"content": "Idea: ..."}'
curl -s -X PATCH localhost:8000/api/captures/1 -d '{"archived": true}'Interactive OpenAPI docs: http://localhost:8000/docs.
Data & storage
A single SQLite file (database.path, default data/captures.db), WAL
mode:
captures(
id INTEGER PRIMARY KEY,
content TEXT NOT NULL,
archived INTEGER NOT NULL DEFAULT 0, -- 0 = active, 1 = archived
created_at TEXT NOT NULL, -- ISO-8601 UTC
updated_at TEXT NOT NULL
);
capture_tags(
capture_id INTEGER NOT NULL REFERENCES captures(id) ON DELETE CASCADE,
tag TEXT NOT NULL
);A capture keeps its full history: editing updates content/updated_at,
archiving flips archived, only DELETE removes the row. Delete the file
(and its -wal/-shm siblings) to wipe the store.
Project layout
capture/
├── settings.yaml # all configuration (tags live here)
├── pyproject.toml
├── LICENSE # MIT
├── src/capture/
│ ├── cli.py # `capture` CLI: run / install / uninstall
│ ├── app.py # FastAPI app: REST API, static UI, MCP mount
│ ├── mcp_server.py # the six MCP tools (streamable HTTP)
│ ├── llm.py # auto-tagging (JSON contract, inbox fallback)
│ ├── db.py # SQLite layer (WAL, capture_tags join table)
│ ├── service.py # background-service install (systemd/launchd/schtasks)
│ ├── config.py # settings.yaml loader (fresh reads for tags)
│ └── static/ # web UI: index.html, style.css, app.js (no build step)
└── data/captures.db # created on first run (gitignored)Development
Run with capture (or python -m capture) and reload the page after
editing src/capture/static/*. Background-service commands work the same
way: capture install / capture uninstall (or python -m capture install).
License
MIT — do whatever you want with it, no warranty.
This server cannot be deployed
Maintenance
Related MCP Connectors
Agent-native notes, tasks, dev-docs, vaults, sync & handoffs. MCP + OpenAPI dual surface.
Read and write your Caliu notes from any MCP client: search, create, tag, remind, attach files.
Google Keep-style notes app with an MCP server for AI agents to read/write notes.
Search, read, and write your Apple Notes from ChatGPT/Claude via a local Mac agent + MCP relay.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables access to a notes knowledge base via MCP, providing tools for keyword search, note creation, and retrieval, along with browseable note resources.11 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables users to create, retrieve, update, delete, and list notes through an MCP server, with persistent JSON storage and atomic writes.MIT
- AlicenseNot gradedqualityCmaintenanceEnables creating, retrieving, updating, deleting, and listing notes through a simple MCP interface, with configurable note storage.MIT
- AlicenseNot gradedqualityCmaintenanceEnables managing a local notes file through add, get, update, delete, and list operations via MCP.MIT