Journaling-focused RAG for Obsidian vaults, optimized for Apple Silicon GPU. Runs as an MCP server with a web dashboard (Serena-inspired). Think Rosebud AI but for your local markdown notes.
Kajet is an old/regional Polish word for a notebook (from French cahier). Once common, now mostly found in dialects or among older generations. The name came from a walk in the snow with the dog (the /touch-grass endpoint was temporarily unavailable due to weather conditions) β the phrase "sprawdziΔ w kajecie" ("check it in the notebook") struck me as an absurdly fitting thing to say to an LLM.
I take a lot of notes in Obsidian and wanted a proper RAG pipeline that actually works for me β local, fast, and tailored to how I use my vault. local-rag was an interesting starting point, but it runs JS-only CPU models. I wanted something optimized for macOS and Apple Silicon GPU, not a glorified grep burning through CPU cycles.
Also: the male urge to write a side-project in Rust was too strong. Nobody talks about the 30 GB target/ folder, but here we are.
- π Semantic search over your entire vault via MCP
searchtool (hybrid vector + full-text) - π§ Local embeddings β AllMiniLM-L6-v2 via candle, Metal GPU on Apple Silicon, with custom model support
- π Unicode normalization β handles the two ways of writing
Δin Unicode: NFC (Δas one character) vs NFD (e+ combining ogonek). Searching for "GdaΕsk" finds "GdaΕsk" even when your filesystem and editor disagree on encoding - β‘ Incremental indexing β only re-embeds changed files (content hashing)
- π Live file watcher β picks up vault changes automatically
- π Note editing β create, edit, append, and modify notes directly via MCP tools (
create_note,edit_note) - π Web dashboard β search playground + live MCP event stream via WebSocket
- βοΈ Cloud storage support β auto-detects cloud-synced vaults (iCloud, OneDrive, Dropbox, Google Drive) and stores LanceDB outside the sync folder. This is critical for performance: indexing 600 files on iCloud takes ~1 file/sec vs ~70s total (~8.5 files/sec) when stored locally on M4 Mac
- π¦ Single binary β frontend embedded at compile time, zero runtime dependencies
Tested platforms:
- macOS (Apple Silicon) β primary target with Metal GPU acceleration
- Linux (x86_64) β experimental CPU fallback
Runtime requirements (rough estimates):
- 16GB RAM (depends on vault size)
- ~500MB disk space for embedding model
- Additional space for LanceDB (varies by vault size)
Building from source:
- 15-40GB free disk space for Rust
target/directory and dependencies - Expect 5-10 minute initial build (LanceDB pulls in large dependency trees)
Recommended:
- macOS 12+ (Monterey) or later for Metal GPU support
- 8GB+ RAM for vaults with 1000+ files
brew install protobufsudo apt install protobuf-compiler libssl-dev build-essential pkg-config
β οΈ Note: The above is AI hallucination. For a working Linux build, see the CI workflow β you'll need to translate dependencies to your favorite distro.
# Clone and build (requires Deno for frontend build)
git clone https://github.com/yourusername/kajet.git
cd kajet
cd frontend && deno install && deno task build && cd ..
cargo build --release
# Binary will be at target/release/kajetkajet includes tools that can modify and delete your notes. Destructive edits are backed up automatically, but this is experimental software. LLM agents can be unpredictable β data loss is a real risk if your agent decides to overwrite files because you didn't say "good morning" or "thank you" nicely enough.
This MCP is for playing around with data you have backed up. Use git, Time Machine, or whatever backup solution you trust. Don't point it at your only copy of anything important.
You've been warned. π
Add to your .claude/mcp.json or Claude Desktop config:
{
"mcpServers": {
"kajet": {
"command": "/path/to/kajet",
"args": ["--vault", "/path/to/your/obsidian/vault"]
}
}
}The dashboard will be available at http://localhost:3579 while the MCP server is running.
Example queries to try in Claude:
- "What notes do I have about machine learning?"
- "Find my thoughts on productivity systems"
- "Show me notes mentioning both Rust and performance"
Goose has a GUI where you can add MCP servers by clicking through the interface.
Protip: Use the path to your built binary from target/release/kajet and pass --vault /path/to/your/markdown/repo as arguments.
kajet --vault ~/Obsidian/Vault
# Dashboard at http://localhost:3579stdin/stdout ββ [MCP stdio] ββ Engine ββ [Axum HTTP :3579] ββ Browser
β
LanceDB
(.kajet/ in vault or ~/Library/Application Support/kajet/)
Stack:
- MCP: Official
rmcpSDK with#[tool]macros - Embeddings: candle (AllMiniLM-L6-v2, Metal GPU on macOS, CPU fallback on Linux)
- Vector DB: LanceDB (embedded, Lance columnar format)
- Frontend: Svelte + Vite, embedded in binary via
rust-embed - File watching: notify for live re-indexing
- HTTP: Axum with WebSocket support
Workspace structure:
crates/coreβ Domain model,Engine, trait definitionscrates/parserβ Markdown parsing, chunking, wikilink extractioncrates/backendβ Concrete implementations (embedder, vector store)crates/indexerβ Incremental indexing pipeline and file watchercrates/mcpβ MCP protocol handlercrates/webβ Axum HTTP server and WebSocket broadcastercrates/writerβ Note creation and editing
Semantic search over the vault using hybrid vector + full-text search.
| Param | Type | Default | Description |
|---|---|---|---|
query |
string | required | Search query |
limit |
number | 5 |
Max results (1-50) |
mode |
string | "hybrid" |
Search mode: "hybrid" (vector + FTS), "vector" (semantic only), "fts" (full-text only) |
Example:
{
"query": "productivity and note-taking",
"limit": 10,
"mode": "hybrid"
}View a document's metadata and content. Accepts full or partial file paths with fuzzy matching.
| Param | Type | Default | Description |
|---|---|---|---|
path |
string | required | Full or partial file path |
content |
string | "summary" |
Content mode: "summary" (first 500 chars), "full", or "slice" |
offset |
number | 0 |
Character offset for "slice" mode |
length |
number | 500 |
Number of characters for "slice" mode |
Edit an existing note with multiple modes (append, prepend, replace, etc.).
| Param | Type | Description |
|---|---|---|
path |
string | Full or partial file path |
content |
string | New content to insert or replace with |
mode |
string | Edit mode: "append", "prepend", "overwrite", "replace_section", "replace_text", "insert_after" |
target_heading |
string | (optional) Target heading for section operations |
old_text |
string | (optional) Exact text to replace (required for replace_text) or anchor for insert_after |
Create a new note with automatic frontmatter generation.
| Param | Type | Description |
|---|---|---|
target |
string | Relative path for the new note (e.g., "Projects/ideas.md") |
content |
string | Markdown body content |
tags |
array | (optional) Tags for frontmatter |
aliases |
array | (optional) Aliases for Obsidian linking |
List all unique tags from the vault with optional filtering.
| Param | Type | Default | Description |
|---|---|---|---|
detail |
string | "counts" |
Detail level: "names", "counts" (tag + doc count), "full" (tag + count + paths) |
folder |
string | (optional) | Filter by folder path prefix |
recursive |
boolean | true |
Include subfolders |
Get current index status: number of documents, chunks, and last indexing time.
Reindex the vault. Without arguments, performs a full vault reindex. With a path argument, reindexes only that specific file.
| Param | Type | Description |
|---|---|---|
path |
string | (optional) Relative path of a specific file to reindex |
- Rust (latest stable, edition 2024)
- Deno (for frontend build) β deno.land
- protobuf (see Prerequisites)
# 1. Build frontend first
cd frontend
deno install
deno task build
cd ..
# 2. Build Rust workspace
cargo build --release
# Binary at target/release/kajet# Run all tests with cargo-nextest (recommended)
cargo nextest run --workspace
# Run tests for a specific crate
cargo nextest run -p kajet-parser
# Run a single test by name
cargo nextest run -E 'test(test_name)'
# Fallback to standard cargo test if nextest not installed
cargo test --workspace# Format check (runs on pre-commit hook)
cargo fmt --check
# Lint (runs on pre-commit hook)
cargo clippy --workspace -- -D warnings
# Typo check (runs on pre-commit hook)
typosPre-commit hooks are managed via Lefthook. Install with:
lefthook install# Use the included script
./run-inspector.sh /path/to/vault
# Or manually
npx @modelcontextprotocol/inspector cargo run -- --vault /path/to/vault- Commits: Use Conventional Commits (
feat:,fix:,refactor:,perf:,docs:,test:,chore:,ci:) - i18n: User-facing strings go through
t!()macro (rust-i18n). Locale files:locales/{en,pl}.toml - Logging levels:
INFO= entry point (query, params, result count)DEBUG= timings and score statsTRACE= raw data (embeddings, scores)- Use
#[tracing::instrument]withskip(self)on search methods
The project uses a Cargo workspace with trait-based dependency injection:
kajet (root binary)
βββ kajet-core # Domain model, Engine, trait definitions
βββ kajet-parser # Markdown parsing, chunking, wikilinks
βββ kajet-backend # Concrete implementations (embedder, vector store)
βββ kajet-indexer # Incremental indexing pipeline + file watcher
βββ kajet-mcp # MCP protocol handler
βββ kajet-web # Axum HTTP server + WebSocket
βββ kajet-writer # Note creation and editing (WIP)
- Advanced filtering (by date, folders, tag combinations)
- Daily notes and templates support
- Tag analysis tools (co-occurrence, edit tags)
- Temporal journal queries (recent context, on this day, get entries)
- Vault structure exploration tool
- Chunk quality scoring and viewer in dashboard
- Inspired by Serena for the MCP + dashboard approach
- Built on LanceDB, candle, and the MCP Rust SDK