Local code intelligence MCP server — semantic search + code graph + impact analysis. Ask code in plain language.
Project description
nova-rag
This project is completely free and open source (MIT). No paid tiers, no premium features, no "contact sales" — everything is included. If nova-rag saves you time, the best way to say thanks is to give it a star on GitHub. It helps others discover the project and keeps development going.
Ask questions about code in plain language. Get answers with full context.
Don't know the function name? Don't need to. Ask "where is payment processing?" and get the function, who calls it, what it calls, and where it lives — in one request.
The only MCP server that combines semantic code search with code graph intelligence. Other code graph servers (CodeGraph, Code Pathfinder, CodeGraphContext) require exact symbol names. nova-rag understands natural language.
You: "how is authentication handled?"
nova-rag: handleAuth() in src/auth/middleware.py:42
Callers: login_endpoint, process_request, verify_session
Callees: verify_token, get_user, logger.error
AuthService class in src/auth/service.py:8
Extends: BaseService
Callers: register_endpoint, oauth_callback
100% local. No API keys. No data leaves your machine.
The Problem
┌─────────────────────────────────────────────────────────────────────────┐
│ You're new on a 3000-file project │
│ You need to fix a payment bug │
│ You don't know ANY function names │
└────────────────────────────────┬────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ Other code servers │ │ nova-rag │
├────────────────────────┤ ├────────────────────────┤
│ │ │ │
│ You: "where is │ │ You: "where is │
│ payment processing?" │ │ payment processing?" │
│ │ │ │
│ Server: ❌ needs exact │ │ Server: ✅ │
│ function name │ │ processStripeWebhook() │
│ │ │ in payments/hook.py │
│ You: grep "payment" │ │ Callers: checkout, │
│ → 200 results... │ │ subscription │
│ You: grep "pay" │ │ Callees: update, │
│ → 500 results... │ │ send_receipt │
│ │ │ │
│ ⏱ 20 minutes later... │ │ ⏱ 10 seconds │
│ Found it! │ │ Full picture. Done. │
└────────────────────────┘ └────────────────────────┘
Quick Start
┌──────────────────────────────────────────────┐
│ Step 1: Install │
│ $ pip install nova-rag │
│ │
│ Step 2: Connect to Claude Code │
│ $ claude mcp add rag -- nova-rag │
│ │
│ Step 3: Ask anything │
│ > "how is authentication handled?" │
│ > "who calls the validate function?" │
│ > "find dead code in src/auth" │
│ > "class hierarchy of UserService" │
└──────────────────────────────────────────────┘
Connect to Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"rag": {
"command": "nova-rag",
"args": []
}
}
}
Connect to VS Code / Cursor
VS Code — .vscode/mcp.json:
{ "servers": { "rag": { "command": "nova-rag" } } }
Cursor — ~/.cursor/mcp.json:
{ "mcpServers": { "rag": { "command": "nova-rag" } } }
Usage Guide — Just Ask in Plain Language
Once connected, you don't need to call tools manually. Just ask Claude Code in natural language — nova-rag handles the rest.
Find code (semantic search)
> where is payment processing?
> how is authentication handled?
> show me the database connection code
nova-rag finds relevant functions even if you don't know their names. Results include callers/callees for full context.
Who calls a function
> who calls handleAuth?
> who uses validate?
Shows every place in the codebase that calls this function, with file paths and line numbers.
What a function calls
> what does processData call?
> what functions does login call inside?
Shows all functions called inside a given function.
Who imports a module
> who imports psycopg2?
> who imports the auth module?
Find dead code
> find unused functions
> find unused code in src/
Lists functions and methods that are never called anywhere.
Impact analysis — what breaks if I change this?
> what is the impact of changing validate?
> what breaks if I change handleAuth?
Shows direct callers, transitive callers, affected files, affected tests, and risk level.
Class hierarchy
> class hierarchy of UserService
> inheritance of DataProcessor
Shows parents (extends/implements) and children.
Recent git changes
> what changed this week?
> what changed in auth last week?
Shows modified files, new/changed symbols, insertions/deletions.
First run
On your first query to a new project:
- Embedding model downloads (~80MB) — once, then cached
- Project indexes (30-120s) — once, then incremental
- File watcher starts — auto-updates index on file changes
All subsequent queries are instant.
What It Does — Visual Guide
The Smart Router
One tool handles everything. It reads your query and routes automatically:
┌──────────────────┐
│ code_search() │
│ "your query" │
└────────┬─────────┘
│
┌─────────────┼─────────────┐
│ Intent │ Detection │
▼ ▼ ▼
┌─────────────┐ ┌────────────┐ ┌──────────────┐
│ "where is │ │ "who calls │ │ "dead code" │
│ error │ │ handleAuth"│ │ │
│ handling?" │ │ │ │ │
└──────┬──────┘ └─────┬──────┘ └──────┬───────┘
│ │ │
▼ ▼ ▼
┌────────────┐ ┌───────────┐ ┌──────────────┐
│ Semantic │ │ Graph │ │ Dead Code │
│ Search │ │ Query │ │ Detection │
│ (hybrid) │ │ (callers) │ │ │
└────────────┘ └───────────┘ └──────────────┘
Also detects: callees, importers, class hierarchy
Works in English and Russian
Hybrid Search — How Two Engines Beat One
Query: "error handling"
│
├───────────────────────────────────────────┐
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ 🧠 Vector Search │ │ 📝 Keyword Search │
│ (Neural Network) │ │ (SQLite FTS5) │
│ │ │ │
│ Understands MEANING │ │ Matches exact WORDS │
│ │ │ │
│ "error handling" → │ │ "error handling" → │
│ finds try/catch, │ │ finds code with │
│ exception handlers, │ │ literal "error" or │
│ error responses │ │ "handling" in text │
│ even without those │ │ │
│ exact words │ │ │
└──────────┬───────────┘ └──────────┬───────────┘
│ rank 1, 2, 3... │ rank 1, 2, 3...
│ │
└────────────┬────────────────────┘
│
▼
┌───────────────────────┐
│ Reciprocal Rank │
│ Fusion (RRF) │
│ │
│ score = 1/(k+rank₁) │
│ + 1/(k+rank₂) │
│ │
│ Best of both worlds │
└───────────┬───────────┘
│
▼
┌───────────────────────┐
│ Results + Graph │
│ │
│ handle_error() │
│ Callers: login, │
│ register │
│ Callees: log, │
│ format_response │
└───────────────────────┘
What each engine catches:
┌──────────────────────────┬──────────────┬──────────────┬──────────────┐
│ Query │ Vector only │ Keyword only │ Hybrid (RRF) │
├──────────────────────────┼──────────────┼──────────────┼──────────────┤
│ "error handling" │ ✅ finds │ ✅ finds │ ✅ best of │
│ │ related code │ exact words │ both │
├──────────────────────────┼──────────────┼──────────────┼──────────────┤
│ "getUserById" │ ⚠️ returns │ ✅ exact │ ✅ exact │
│ │ fetchUser │ match │ match #1 │
├──────────────────────────┼──────────────┼──────────────┼──────────────┤
│ "database connection" │ ✅ finds │ ❌ no match │ ✅ found │
│ │ get_pool() │ (wrong words)│ via vector │
├──────────────────────────┼──────────────┼──────────────┼──────────────┤
│ "auth middleware setup" │ ✅ semantic │ ⚠️ partial │ ✅ combined │
│ │ match │ match │ score │
└──────────────────────────┴──────────────┴──────────────┴──────────────┘
Code Graph — What Gets Extracted
Source file: src/auth/middleware.py
┌─────────────────────────────────────────────────┐
│ from auth.tokens import verify_token │──── Import
│ from db import get_user │──── Import
│ │
│ class AuthMiddleware(BaseMiddleware): │──── Symbol (class)
│ │ │ + Inheritance
│ │ def handleAuth(self, request): │──── Symbol (method)
│ │ token = verify_token(request.token) │──── Call
│ │ user = get_user(token.user_id) │──── Call
│ │ if not user: │
│ │ logger.error("No user") │──── Call
│ │ return user │
│ │ │
└─────┴────────────────────────────────────────────┘
Extracted graph for this file:
┌─────────────┐ ┌──────────────────────────────────────┐
│ Symbols │ │ AuthMiddleware (class, line 4) │
│ │ │ handleAuth (method, line 6) │
└─────────────┘ └──────────────────────────────────────┘
┌─────────────┐ ┌──────────────────────────────────────┐
│ Calls │ │ handleAuth → verify_token (line 7) │
│ │ │ handleAuth → get_user (line 8) │
│ │ │ handleAuth → logger.error (line 10) │
└─────────────┘ └──────────────────────────────────────┘
┌─────────────┐ ┌──────────────────────────────────────┐
│ Imports │ │ auth.tokens → verify_token │
│ │ │ db → get_user │
└─────────────┘ └──────────────────────────────────────┘
┌─────────────┐ ┌──────────────────────────────────────┐
│ Inheritance │ │ AuthMiddleware extends BaseMiddleware│
└─────────────┘ └──────────────────────────────────────┘
Graph Queries — What You Can Ask
┌──────────────────────────────────────────────────────────────┐
│ handleAuth() │
│ │
│ ◀── CALLERS ──────────────── CALLEES ──▶ │
│ │
│ login_endpoint() verify_token() │
│ process_request() get_user() │
│ test_auth_flow() logger.error() │
│ │
│ ◀── IMPORTERS ──────────── HIERARCHY ──▶ │
│ │
│ routes/auth.py BaseMiddleware │
│ middleware/main.py ▲ │
│ tests/test_auth.py │ extends │
│ AuthMiddleware │
│ ▲ │
│ │ extends │
│ AdminMiddleware │
└──────────────────────────────────────────────────────────────┘
direction="callers" → left side (who calls this?)
direction="callees" → right side (what does this call?)
direction="importers"→ bottom-left (who imports this?)
direction="hierarchy"→ bottom-right (inheritance tree)
direction="both" → callers + callees
depth=2 → callers of callers (transitive)
Transitive Callers (depth=2)
rag_graph("verify_token", direction="callers", depth=2)
verify_token()
│
◀── handleAuth() ← depth 1 (direct callers)
│ │
│ ◀── login_endpoint() ← depth 2 (callers of callers)
│ ◀── process_request()
│ ◀── test_auth_flow()
│
◀── refresh_session() ← depth 1
│
◀── session_middleware() ← depth 2
◀── test_refresh()
Dead Code Detection
rag_deadcode()
All symbols: Symbols with callers: Dead code:
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ handleAuth │ │ handleAuth ✅ │ │ │
│ verify_token │ │ verify_token ✅│ │ │
│ get_user │ → │ get_user ✅ │ → │ old_validate │
│ old_validate │ │ │ │ format_v1 │
│ format_v1 │ │ │ │ LegacyParser │
│ LegacyParser │ │ │ │ │
│ main │ │ main (skip) │ │ 3 functions │
│ test_auth │ │ test_ (skip) │ │ 0 callers │
└───────────────┘ └───────────────┘ └───────────────┘
Filters out: main, __init__, setUp, tearDown, test_*
Class Hierarchy
rag_graph("DataProcessor", direction="hierarchy")
┌─────────────────┐
│ BaseProcessor │
│ (base.py:5) │
└────────┬────────┘
│ extends
┌────────┴────────┐
│ DataProcessor │ ◀── you asked about this
│ (processor.py:12)│
└────────┬────────┘
│ extends
┌─────────────┼─────────────┐
┌────────┴────────┐ ┌────────┴────────┐
│AdvancedProcessor│ │ StreamProcessor │
│ (advanced.py:8) │ │ (stream.py:15) │
└─────────────────┘ └──────────────────┘
Indexing Pipeline — What Happens Inside
Your project (3000 files)
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 1: File Discovery │
│ ├── Walk all files recursively │
│ ├── Skip: .git, node_modules, __pycache__, binaries │
│ ├── Respect .gitignore │
│ └── Skip files > 1MB │
│ Result: 1200 indexable files │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 2: Parallel Processing (8 threads) ⚡ FAST │
│ │
│ Thread 1: auth.py ──┐ │
│ Thread 2: db.py ────┤ Each thread: │
│ Thread 3: api.py ───┤ ├── Read file │
│ Thread 4: utils.py ─┤ ├── Tree-sitter parse │
│ Thread 5: models.py ┤ ├── Extract chunks (functions, classes) │
│ Thread 6: tests.py ─┤ ├── Extract graph (calls, imports) │
│ Thread 7: views.py ─┤ └── Extract inheritance │
│ Thread 8: forms.py ─┘ │
│ │
│ Result: chunks + graph data for all files │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 3: Embedding (sequential — model not thread-safe) │
│ │
│ all-MiniLM-L6-v2 model (384-dim vectors) │
│ Batch size: 64 chunks at a time │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │Batch1│→│Batch2│→│Batch3│→│Batch4│→ ... │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Phase 4: Storage (sequential — SQLite not thread-safe) │
│ │
│ ┌─────────────┐ ┌──────────────────────────────────┐ │
│ │ FAISS │ │ SQLite │ │
│ │ │ │ │ │
│ │ 384-dim │ │ chunks ──── FTS5 keyword index │ │
│ │ vectors │ │ symbols ─── function/class defs │ │
│ │ for cosine │ │ calls ───── caller→callee edges │ │
│ │ similarity │ │ imports ─── file→module edges │ │
│ │ │ │ inheritance─ parent→child edges │ │
│ └─────────────┘ └──────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
After Indexing — File Watcher Keeps It Fresh
┌──────────────┐
│ You edit │
│ auth.py │
└──────┬───────┘
│ filesystem event
▼
┌──────────────┐ 5 sec ┌───────────────────────┐
│ Watchdog │──debounce──▶ │ Incremental reindex │
│ Observer │ │ Only changed files │
└──────────────┘ └───────────────────────┘
No manual rag_index needed. Always fresh.
All Tools Reference
code_search — Smart Router (recommended)
One tool for everything. Auto-detects intent from your query.
code_search("where is error handling?") → intent: search
code_search("who calls handleAuth?") → intent: callers
code_search("what does processData call?") → intent: callees
code_search("who imports psycopg2?") → intent: importers
code_search("dead code in src/auth") → intent: deadcode
code_search("class hierarchy of UserService") → intent: hierarchy
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query |
string | yes | — | Natural language query |
path |
string | no | cwd | Project directory |
top_k |
integer | no | 10 | Max results (for search) |
path_filter |
string | no | null | Filter file paths |
language |
string | no | null | Filter by language |
rag_search — Direct Hybrid Search
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query |
string | yes | — | Search query |
path |
string | no | cwd | Project directory |
top_k |
integer | no | 10 | Max results |
path_filter |
string | no | null | Filter file paths |
language |
string | no | null | Filter by language |
rag_graph — Direct Graph Navigation
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | yes | — | Symbol name |
path |
string | no | cwd | Project directory |
direction |
string | no | "both" |
"callers", "callees", "both", "importers", "hierarchy" |
depth |
integer | no | 1 | Traversal depth (1-2) |
rag_deadcode — Dead Code Detection
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | no | cwd | Project directory |
path_filter |
string | no | null | Scope to specific path |
rag_index — Index / Reindex
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | no | cwd | Directory to index |
force |
boolean | no | false | Full rebuild |
rag_status — Index Status
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | no | cwd | Project directory |
Returns: indexed_files, total_chunks, symbols, calls, imports, inheritances, index_size_mb.
rag_watch — File Watcher
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | no | cwd | Directory to watch |
Supported Languages
┌──────────────────┬────────────────────────────┬──────────────────────────┐
│ Language │ Chunks │ Graph │
├──────────────────┼────────────────────────────┼──────────────────────────┤
│ Python │ functions, classes, defs │ calls, imports, inherit │
│ TypeScript / TSX │ functions, classes, ifaces │ calls, imports, inherit │
│ JavaScript / JSX │ functions, classes, arrows │ calls, imports │
│ C# │ methods, classes, structs │ calls, imports, inherit │
│ Go │ functions, methods, types │ calls, imports │
│ Rust │ functions, impls, traits │ calls, imports, inherit │
│ Java │ methods, classes, ifaces │ calls, imports, inherit │
├──────────────────┼────────────────────────────┼──────────────────────────┤
│ Other text files │ 60-line sliding window │ — │
└──────────────────┴────────────────────────────┴──────────────────────────┘
All languages also get: file header chunks (imports, docstrings)
+ symbol name extraction from AST
Use with CLAUDE.md
Add to your project's CLAUDE.md:
## Code Search (nova-rag)
This project has a local code intelligence index. Prefer `code_search` over Grep for questions about the codebase:
- "where is payment processing?" → finds relevant functions with full context
- "who calls handleAuth?" → shows all call sites
- "dead code in src/" → finds unused functions
- "class hierarchy of UserService" → shows inheritance tree
Search results include callers/callees — use them to understand the full picture.
For exact string matches (TODOs, error messages), use Grep as usual.
Comparison
nova-rag vs Code Graph Servers
┌────────────────────────────┬──────────────────┬──────────────────────┐
│ │ nova-rag │ Code Graph Servers │
│ │ │ (CodeGraph, Code │
│ │ │ Pathfinder, etc.) │
├────────────────────────────┼──────────────────┼──────────────────────┤
│ "where is error handling?" │ ✅ Understands │ ❌ Needs exact name │
│ "who calls handleAuth?" │ ✅ Yes │ ✅ Yes (strength) │
│ Semantic search │ ✅ Hybrid V+K │ ❌ No │
│ Dataflow analysis │ ❌ No │ ✅ Code Pathfinder │
│ Impact analysis │ ⚠️ Basic depth=2 │ ✅ Full blast radius │
│ Languages │ 7 │ Up to 64 │
│ Dependencies │ ~2-3GB (PyTorch) │ 20MB-200MB │
│ Setup │ pip install │ Varies │
└────────────────────────────┴──────────────────┴──────────────────────┘
They complement each other:
nova-rag = explore & discover → Code Pathfinder = deep analysis
"find the right symbols" "analyze those symbols deeply"
nova-rag vs RAG Document Servers
┌────────────────────────────┬──────────────────┬──────────────────────┐
│ │ nova-rag │ Document RAG │
│ │ │ (mcp-local-rag, │
│ │ │ ChromaDB-MCP) │
├────────────────────────────┼──────────────────┼──────────────────────┤
│ Code graph │ ✅ Full │ ❌ No │
│ Dead code detection │ ✅ Yes │ ❌ No │
│ Tree-sitter chunking │ ✅ 7 languages │ ❌ Text splitting │
│ Smart router │ ✅ Auto-detects │ ❌ No │
│ Class hierarchy │ ✅ Yes │ ❌ No │
│ Designed for │ Code │ Documents (PDF, MD) │
└────────────────────────────┴──────────────────┴──────────────────────┘
Configuration
| Variable | Default | Description |
|---|---|---|
NOVA_RAG_MODEL |
all-MiniLM-L6-v2 |
Sentence-transformers model |
NOVA_RAG_CHUNK_SIZE |
60 |
Max lines per sliding window chunk |
NOVA_RAG_CHUNK_OVERLAP |
10 |
Overlap between chunks |
NOVA_RAG_BATCH_SIZE |
64 |
Embedding batch size |
NOVA_RAG_DATA_DIR |
~/.nova-rag |
Index storage directory |
Storage layout:
~/.nova-rag/
├── a1b2c3d4e5f6/ ← project hash
│ ├── faiss.index ← vector embeddings
│ └── meta.db ← SQLite (chunks, graph, FTS5)
└── f6e5d4c3b2a1/ ← another project
├── faiss.index
└── meta.db
One index per project. Persisted between sessions.
Troubleshooting
First search is slow: Model (~80MB) downloads on first use. Pre-loaded in background on subsequent starts.
High memory (~400MB): That's the embedding model. Use NOVA_RAG_MODEL=all-MiniLM-L12-v2 for smaller footprint.
Index stale: File watcher auto-updates. Force full rebuild: rag_index(force=true).
Server won't start: Check which nova-rag or run python -m nova_rag for error output.
Development
git clone https://github.com/yourusername/nova-rag.git
cd nova-rag
pip install -e ".[dev]"
pytest tests/ -v # 99 tests
Project Structure
src/nova_rag/
├── server.py MCP server, 11 tools including smart router
├── searcher.py Smart router, hybrid search, graph queries, dead code
├── indexer.py Multithreaded file processing + embedding
├── chunker.py Tree-sitter AST parsing (7 languages) + fallback
├── graph.py Code graph: symbols, calls, imports, inheritance
├── store.py FAISS + SQLite (FTS5 + graph + inheritance)
├── watcher.py File watcher for auto re-indexing
└── config.py Environment-based configuration
Contributing
- Fork the repo
- Create a feature branch
- Write tests for new functionality
- Ensure all 99 tests pass (
pytest tests/ -v) - Submit a PR
License
MIT — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nova_rag-0.1.0.tar.gz.
File metadata
- Download URL: nova_rag-0.1.0.tar.gz
- Upload date:
- Size: 51.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d9561d972358d2c0a1457375ff966edfbfaa09f05d11aaccc8ada848490b51e
|
|
| MD5 |
97c791ff9256df4334708a77f1fa4c0a
|
|
| BLAKE2b-256 |
6f818384a0997fe51abb870482ca8286d3af88c12af0d67db877b3a12830dc20
|
File details
Details for the file nova_rag-0.1.0-py3-none-any.whl.
File metadata
- Download URL: nova_rag-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a358c5dac5972df6dc5c312a4418ca6e5b2c3f6b340004b1125aee39c88f754
|
|
| MD5 |
f01ba9ad2cf2a87a87af841d75347aab
|
|
| BLAKE2b-256 |
c6386e0c48d07167678ebc46c571d98e77e5d52d6ff0ef4eb4c00256d0a490fa
|