使用说明
local-rag
A fully local, privacy-preserving RAG (Retrieval Augmented Generation) system for macOS. Indexes personal knowledge from multiple sources into a single SQLite database with hybrid vector + full-text search, plus a relation graph so a result can be expanded into what it is connected to — not only into what resembles it. Runs as a menu bar app with built-in MCP server so Claude Desktop and Claude Code can query your personal knowledge base directly.
Supported Sources
| Source | Collection Type | What Gets Indexed |
|---|---|---|
| Obsidian | system | Vault files — .md, .pdf, .docx, .html, .txt, .epub |
| eM Client | system | Emails — subject, body, sender, recipients, date, folder |
| Calibre | system | Ebook metadata + content — EPUB/PDF with author, tags, series |
| NetNewsWire | system | RSS articles — title, author, content, feed name |
| Code Repos | code | Git repos — paths can be direct repos or parent dirs (auto-discovered recursively). Tree-sitter AST split-then-merge (cAST) chunking + commit history |
| Project Docs | project | Any folder of documents dispatched to the correct parser by extension |
Installation
From GitHub Releases
Download the latest .dmg from Releases, open it, and drag local-rag to Applications.
Since the app is not code-signed, bypass Gatekeeper on first launch:
xattr -cr /Applications/local-rag.app
Build from Source
# Prerequisites
brew install ollama go
ollama pull bge-m3
# Optional: OCR support for scanned PDFs
brew install tesseract tesseract-lang
# Build
git clone https://github.com/sebastianhutter/local-rag.git
cd local-rag
make build # binary at bin/local-rag
make app # macOS .app bundle at bin/local-rag.app
make dmg # DMG installer at bin/local-rag.dmg
Requires Go 1.26+, CGO enabled (for SQLite), and macOS (for sips/iconutil/hdiutil). Tesseract is optional — only needed for OCR on scanned/image-only PDFs.
Quick Start
- Configure sources — edit
~/.local-rag/config.json(seeconfigs/config.example.jsonfor a fully annotated template) or use the Settings GUI:
{
"obsidian_vaults": ["~/Documents/MyVault"],
"calibre_libraries": ["~/CalibreLibrary"],
"repositories": {
"my-org": ["~/Repository/my-org"]
},
"projects": {
"client-docs": ["~/Documents/client-project/specs"]
}
}
- Index your content:
local-rag index obsidian
local-rag index email
local-rag index calibre
local-rag index rss
local-rag index code my-org --history
local-rag index all # everything at once
- Search:
local-rag search "kubernetes deployment strategy"
local-rag search "invoice from supplier" --collection email
local-rag search "API specification" --type pdf --top 20
local-rag search "s3 bucket policy" --collection code --path infrastructure/modules
- Prune — indexing file-backed collections (obsidian, code, project) removes stale entries automatically, so deleted files disappear from search without extra steps. To prune on demand:
local-rag prune # all collections, including email/calibre/rss
local-rag prune obsidian # a single collection
GUI
Launch local-rag with no arguments (or local-rag gui) to start the menu bar app. There is no Dock icon — it lives entirely in the macOS menu bar.
Menu bar features:
- MCP server toggle — start/stop the built-in MCP server (Streamable HTTP, default port 31123)
- Status display — collection and chunk counts, indexing progress
- Index menu — trigger indexing for individual collections or all at once
- Settings — configure sources, embedding model, Ollama hosts (ordered endpoints; first reachable one that has the model is used, else local) and embedding batch size, search weights, MCP port, auto-reindex interval, start-on-login
- Log viewer — live scrolling log output with auto-scroll toggle
- Auto-reindex — periodically re-index all sources on a configurable interval
- macOS notifications — notifies when indexing completes or errors occur
MCP Integration
local-rag exposes 5 MCP tools: rag_search, rag_list_collections, rag_collection_info, rag_index, and rag_prune.
The rag_search tool supports a metadata_filter parameter — a JSON object of key-value pairs for filtering by arbitrary metadata fields (e.g. {"source": "jira", "issue_key": "PROJ-123"}) — and a path parameter to scope results to a subfolder or repo by a case-insensitive substring of the source path (e.g. "infrastructure/modules"). Its collection parameter accepts either a collection name or a collection type (system, project, code), so a search can be scoped to all code repos at once.
GUI Mode (Streamable HTTP)
When running as a menu bar app, the MCP server uses the Streamable HTTP transport on http://127.0.0.1:31123/mcp. The older SSE transport (/sse) was removed: it is deprecated in the MCP spec, and clients that speak only Streamable HTTP get a 405 from it. Requests carrying a non-localhost Origin header are rejected, so a web page in your browser cannot query the server.
Claude Code — add to your project's .mcp.json:
{
"mcpServers": {
"local-rag": {
"type": "http",
"url": "http://127.0.0.1:31123/mcp"
}
}
}
Claude Desktop — add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"local-rag": {
"command": "/path/to/local-rag",
"args": ["serve"]
}
}
}
Standalone Mode (stdio)
For use as a subprocess without the GUI:
{
"mcpServers": {
"local-rag": {
"command": "/path/to/local-rag",
"args": ["serve"]
}
}
}
CLI Reference
Indexing
local-rag index obsidian [--vault/-V PATH]... Index Obsidian vaults
local-rag index email Index eM Client emails
local-rag index calibre [--library/-l PATH]... Index Calibre ebook libraries
local-rag index rss Index NetNewsWire RSS articles
local-rag index code [NAME] [--history] Index code repositories; omit NAME for all
local-rag index project [NAME] Index project(s) from config; omit NAME for all
local-rag index all Index all configured sources
All index commands accept --force to re-index everything regardless of change detection, --no-prune to skip the automatic pruning pass, and --no-graph to skip the relation-graph rebuild that otherwise runs afterwards.
Indexing obsidian, code, project and all prunes stale sources first — entries whose file no longer exists on disk are removed before the run, so deleted and moved files do not linger in search results.
Pruning
local-rag prune [COLLECTION] Remove sources whose originals are gone; omit COLLECTION for all
local-rag prune --vectors Remove orphaned embeddings (vectors with no surviving document)
Pruning covers deleted files, emails removed from eM Client, articles purged from NetNewsWire, books removed from Calibre, and files deleted from repositories. -y skips confirmation.
Searching
local-rag search QUERY [flags]
Flags:
-c, --collection STRING Filter by collection name
--type STRING Filter by source type (markdown, pdf, email, code, ...)
--path STRING Filter by source path (case-insensitive substring, e.g. a subfolder or repo)
--from STRING Filter by email sender
--author STRING Filter by book author
--after YYYY-MM-DD Only results after this date
--before YYYY-MM-DD Only results before this date
-m, --meta KEY=VALUE Filter by metadata field (repeatable)
--top INT Number of results (default: 10)
Collections
local-rag collections list List all collections with counts
local-rag collections info NAME Show collection details
local-rag collections delete NAME [-y] Delete a collection and all its data
local-rag collections export NAME Export collection metadata as JSON
local-rag collections paths list NAME List configured paths for a collection
local-rag collections paths add NAME PATH... Add paths to a collection in config
local-rag collections paths remove NAME PATH... Remove paths from a collection in config
local-rag collections paths update NAME \ Rewrite path prefixes in-place
--old-prefix OLD --new-prefix NEW (config paths + source paths in DB)
All collection paths are stored in config.json. The paths commands work for all collection types — obsidian vaults, calibre libraries, repositories, and projects.
If you move files to a new location, use paths update to rewrite all paths without re-indexing:
local-rag collections paths update "Project Alpha" \
--old-prefix ~/docs/specs --new-prefix ~/new-location/specs
Relation Graph
local-rag graph rebuild Derive relations from indexed content
local-rag graph rebuild --origin NAME Rebuild one origin, leaving the others alone
local-rag graph stats Edge counts by origin and relation, plus top hubs
local-rag neighbors SOURCE What a source is connected to (id or path substring)
neighbors accepts --rel, --origin, --hops (1 or 2), --hub-cap and --top.
The graph records relations parsed from the content — Obsidian wikilinks and frontmatter
properties, Confluence page hierarchy, Jira issue hierarchy, Terraform module dependencies,
and mentions of a ticket key across mail, notes and commits. It exists to close a gap search cannot: a note that never uses your query's wording
but links to one that does is unreachable at any top_k, and one hop away here. Replaying 60
real queries, 78% gained at least one document that was unreachable at top_k 60.
Nothing calls a model — a rebuild is a scan of data already in the database, about a minute
for ~28,000 edges — and it runs automatically after each index. Agents use it through the
rag_neighbors MCP tool.
See docs/relation-graph.md for the design, the measurements, and why hub suppression is a precondition rather than a refinement.
Other
local-rag status Database stats, collection counts, Ollama status
local-rag graph rebuild Rebuild the relation graph (see above)
local-rag prune [NAME] Remove stale sources (see Pruning above)
local-rag serve [--port N] Start MCP server (stdio, or Streamable HTTP at /mcp on given port)
local-rag gui Start menu bar app (default when no subcommand)
local-rag --version Print version
local-rag -v, --verbose Enable debug logging (global flag)
Configuration
Config file: ~/.local-rag/config.json
| Key | Default | Description |
|---|---|---|
db_path |
~/.local-rag/rag.db |
SQLite database location |
embedding_model |
bge-m3 |
Ollama embedding model |
embedding_dimensions |
1024 |
Embedding vector dimensions |
embedding_hosts |
(unset → localhost) | Ordered Ollama hosts; first reachable one that has the model is used (else local). OLLAMA_HOST env overrides. |
embedding_batch_size |
32 |
Texts per Ollama embedding request; higher (e.g. 128) improves GPU throughput. |
embedding_workers |
4 |
Embedding requests in flight at once; raise for a fast remote host, 1 to serialise. |
embedding_num_batch |
0 (server default) |
Ollama num_batch option. Raise to the model context (e.g. 8192) if the log shows inputs rejected as too large to process. |
chunk_size_tokens |
500 |
Chunk size in whitespace-separated words |
chunk_overlap_tokens |
50 |
Overlap between chunks |
obsidian_vaults |
[] |
Paths to Obsidian vaults |
obsidian_exclude_folders |
[] |
Folders to skip in vaults |
emclient_db_path |
~/Library/Application Support/eM Client |
eM Client database path |
calibre_libraries |
[] |
Paths to Calibre libraries |
netnewswire_db_path |
(auto-detected) | NetNewsWire database path |
repositories |
{} |
Map of collection name to repo/directory paths (directories are scanned recursively for git repos) |
projects |
{} |
Map of project name to document paths |
disabled_collections |
[] |
Collection names to skip during indexing |
skip_cloud_placeholders |
true |
Skip cloud-only files (OneDrive/iCloud/Google/Synology on-demand placeholders) instead of downloading them |
git_history_in_months |
6 |
How far back to index commit history |
git_commit_subject_blacklist |
[] |
Skip commits whose subject starts with any of these strings (prefix match) |
search_defaults.top_k |
10 |
Default number of search results |
search_defaults.rrf_k |
60 |
Reciprocal Rank Fusion parameter |
search_defaults.vector_weight |
0.7 |
Weight for vector similarity |
search_defaults.fts_weight |
0.3 |
Weight for full-text search |
ocr.enabled |
false |
Enable OCR fallback for scanned PDFs |
ocr.languages |
["eng"] |
Tesseract languages (e.g. ["eng","deu"]) |
ocr.max_pages |
50 |
Skip OCR if PDF exceeds this page count |
ocr.max_file_size_mb |
100 |
Skip OCR if file exceeds this size |
ocr.min_word_count |
10 |
OCR pages with fewer extracted words |
gui.auto_start_mcp |
true |
Start MCP server when GUI launches |
gui.mcp_port |
31123 |
MCP HTTP server port |
gui.auto_reindex |
false |
Enable periodic re-indexing |
gui.auto_reindex_interval_minutes |
60 |
Minutes between auto-reindex runs |
gui.start_on_login |
false |
Register as login item via launchd |
Tech Stack
| Component | Choice | Notes |
|---|---|---|
| Language | Go 1.26+ | CGO required for SQLite |
| Database | SQLite + sqlite-vec + FTS5 | Single file, no server |
| Embeddings | Ollama + bge-m3 (1024d) | Fully local, no API keys |
| GUI | Fyne v2 + systray | macOS menu bar app |
| MCP | mcp-go | Streamable HTTP and stdio transports |
| go-pdfium (WASM/Wazero) | No CGO needed for PDF | |
| PDF OCR | tesseract (optional) | Fallback for scanned/image-only PDFs |
| DOCX | archive/zip + encoding/xml | Word document extraction (.docx, .dotx) |
| Code parsing | go-tree-sitter | 13 languages; AST split-then-merge (cAST) chunking |
| CLI | Cobra | Subcommands, flags, help |
| HTML cleanup | golang.org/x/net/html | Strip tags from email/RSS |
Tree-sitter Languages
Structural splitting (functions, classes, methods): Python, Go, HCL/Terraform, TypeScript, TSX, JavaScript, Rust, Java, C, C++, C#, Ruby, Bash.
Full-file parsing: YAML, TOML, SQL, HTML, CSS, Dockerfile, Markdown.
Plaintext fallback: JSON, TXT, CSV, RST, XML, SCSS, Makefile.
Building & Development
make build # Build binary to bin/local-rag
make test # Run tests (requires -tags sqlite_fts5)
make test-v # Verbose tests
make lint # golangci-lint
make tidy # go mod tidy
make app # Build macOS .app bundle
make dmg # Build DMG installer
make clean # Remove bin/
Pass VERSION to inject version at build time:
make build VERSION=1.2.3
bin/local-rag --version # local-rag version 1.2.3
Architecture
cmd/local-rag/ CLI entry point (Cobra) — one cmd_*.go per command group
internal/
config/ Configuration loading and defaults
db/ SQLite + sqlite-vec + FTS5 setup and migrations
embeddings/ Ollama embedding client and host resolution
chunker/ Text chunking strategies
search/ Hybrid search engine (vector + FTS + RRF)
parser/ File parsers (markdown, pdf, docx, epub, html, code, ...)
indexer/ Source indexers, shared batching, pruning
mcp/ MCP server (tools, Streamable HTTP, stdio)
gui/ Fyne menu bar app, settings, log viewer
configs/
config.example.json Annotated configuration template
docs/ Architecture, hybrid search/RRF, relation graph, Ollama, eM Client schema
scripts/
build-app.sh Create macOS .app bundle
build-dmg.sh Create DMG installer
.github/workflows/
release.yml Tagged release build
Deeper notes live in docs/: architecture, hybrid search and RRF, the relation graph, Ollama and embeddings, eM Client schema.
License
This project is licensed under the MIT License.
All dependencies use permissive licenses (MIT, BSD, Apache 2.0). PDF parsing uses go-pdfium (MIT) with the PDFium library running in a WASM sandbox (BSD 3-Clause).