Architecture
How the daemon, extension, and MCP server relate.
Chrome extension ──HTTP (Bearer token)──▶ Daemon ──▶ SQLite ─┐ (side panel) :7331 ──▶ Chroma │ │ shared AI client ──stdio──▶ MCP server ─────────────────▶ SQLite ───┤ on-disk (spawned per session) (direct) ─▶ Chroma ─┘ storesTwo readers, two stores, no coupling
- SQLite (
bookmarks.db) — collections, bookmarks, chunk text, saved page HTML, index status. - Chroma (
chroma/) — one vector collection per bookmark collection.
The daemon owns writes: it runs the indexing pipeline and exposes the REST API the extension uses. It is the only process that fetches pages.
The MCP server is read-only and reaches SQLite and Chroma directly. It never calls the daemon, needs no API token, and works even when the daemon is stopped (as long as something has already indexed the collection). Editors spawn one MCP process per session, so several run at once — see Tracing.
Why the split
The extension talks to a long-lived local server because saving a page needs a server-side fetch and a background job queue. The MCP server is short-lived and latency-sensitive, so it skips HTTP and reads the stores it needs. Keeping them decoupled means an MCP query never waits on the daemon, and the daemon never blocks on an editor.
Next
- Data model — collections, bookmarks, chunks
- Indexing pipeline — from save to searchable
- Security model — API token, SSRF, trust boundaries
Last updated Sep 9, 2026