Stellaris MCP
<p align="center">
<img src="assets/banner.jpeg" alt="Stellaris MCP" width="100%" />
</p>
# Stellaris MCP
An MCP server that combines **semantic search** (pluggable embeddings + LanceDB) with **AST-based code exploration** (tree-sitter) for AI agents.
Search your codebase with natural language, browse file structures, inspect symbol outlines, and retrieve exact source code — all through the Model Context Protocol.
> [Version francaise / French version](README.fr.md)
## Features
- **Routed search** — identifier / dotted / quoted string → FTS only (never embeds; a miss is empty, not a vector guess). Intent → hybrid FTS + vectors + RRF. Language keywords refused.
- **Pluggable embeddings** — OpenAI (default), Voyage AI (`voyage-code-3`, best on code), or Ollama (fully local, no internet required)
- **Optional re-ranking** — Voyage `rerank-2` or Cohere `rerank-v3.5` post-RRF pass for +15-30% top-5 precision
- **Dependency graph** — resolves imports to real file paths, tracks file→file dependencies
- **Blast radius analysis** — BFS traversal to find what would break if you change a file
- **Architecture boundaries** *(v4.4)* — enforce layer rules (`stellaris.boundaries.json`) at index time; violations surfaced via `get_boundary_violations` with zero runtime cost
- **Doc/spec linking** *(v4.4)* — markdown `` `backtick-quoted` `` symbol references are linked to their definitions, queryable via `find_doc_references`
- **Context-overflow protection** *(v4.4)* — tier-aware result limits + automatic truncation with `_truncated` metadata, configured by `STELLARIS_CONTEXT_WINDOW` (small/medium/large/massive tiers)
- **MCP Prompts** — 5 guided workflows (`/nova_explore`, `/nova_find`, `/nova_file`, `/nova_review`, `/nova_usage`)
- **Usage dashboard** — tracks Claude Code token consumption and estimated API cost in real time, with cache analytics and task category breakdown
- **Token breakdown** — see where tokens go: by task category (coding, debugging, feature…), MCP server, and core tool — inspired by [codeburn](https://github.com/AgentSeal/codeburn)
- **Index integrity checker** — automatically purges orphaned chunks and stale meta entries at every startup
- **Auto-reindex hook** — Grok installs it on `npm install`; Claude Code needs a `PostToolUse` snippet (see below)
- **AST exploration**: file tree, symbol outlines, source extraction — zero API calls
- **Context-aware**: imports, sibling symbols, and TODO/FIXME warnings included automatically
- **Incremental indexing**: only changed files are re-embedded
- **Safe by default**: no auto-indexing until you explicitly run `reindex` for the first time
- **Auto-indexing** on subsequent startups (opt-in via `.stellarisrc`)
- **28 file extensions**: TS, JS, Python, Go, Rust, PHP, Java, Ruby, HTML, CSS, Astro, Vue, Svelte, SCSS, JSON, YAML, SQL, GraphQL, Prisma, TOML, QML, shell, and more
- **Graceful degradation**: works without any API key (AST tools still available)
## How agents should search
Grep/rg wins on a **known name** for occurrences. Stellaris wins for **intent**, ranked **definition**, and large-file outline/symbol. Do not send every lookup through hybrid `search_code`.
| Need | Tool |
|------|------|
| Identifier / exact string, definition | `search_code` (FTS, no vector) |
| All occurrences, logs, lockfiles, CSS | grep / rg |
| Intent / name unknown | `search_code` (hybrid) |
| File known, ≳150 lines | `get_file_outline` then `get_symbol` (one body) |
| File known, small | Read the file |
| Identifier miss (`skipped_vector: true`, empty) | grep / rg — do not retry as intent |
Copy the agent skill so this routing is loaded automatically:
[`skills/stellaris-explore/SKILL.md`](skills/stellaris-explore/SKILL.md)
- **Grok:** `~/.grok/skills/stellaris-explore/SKILL.md` or `<project>/.grok/skills/stellaris-explore/SKILL.md`
- **Claude Code:** `~/.claude/skills/stellaris-explore/SKILL.md` or `<project>/.claude/skills/stellaris-explore/SKILL.md`
MCP prompts `/nova_explore` and `/nova_find` already follow this contract.
## Benchmark: intent / multi-file (not a known identifier)
Tested on a real-world Astro project (341 files, 430 chunks indexed), on **intent** questions (auth flows, payment, i18n) — not `search_code("usePermissions")`:
| Metric | Without Stellaris | With Stellaris | Improvement |
|--------|-------------------|----------------|-------------|
| Tool calls (avg) | 5.0 | **1.5** | **-70%** |
| Full files read (avg) | 2.8 | **0** | **-100%** |
| Tokens consumed | ~12 000 | ~**2 500** | **-80%** |
| Precision | Variable (noisy grep results) | **High** (targeted previews) | |
On a name you already have, grep/rg is cheaper for occurrences. `search_code` FTS is for the ranked definition (no embedding).
## Tools (16)
### Semantic search (requires an embedding API key, or Ollama locally)
| Tool | Description |
|------|-------------|
| `search_code` | Identifier / exact string → FTS only (never embeds; empty = no symbol). Intent → hybrid. Optional `path` = another indexed project. Returns `query_kind`, `skipped_vector`, 1 hit/file, short previews, `next` hint. Default filter is code extensions; pass `extensions` for html/css/json. |
| `search_docs` | Docs search. Same `path` contract as `search_code` / `reindex`. |
| `reindex` | Incremental re-indexing of the project. Builds vector index, FTS index, and dependency graph. Use `force=true` after switching embedding providers. |
| `reindex_file` | Re-index a single file by absolute path. Used by auto-reindex hooks after edits. |
### Structural exploration (no API calls)
| Tool | Description |
|------|-------------|
| `get_file_tree` | Project file tree with language stats. |
| `get_file_outline` | List symbols in a file with line ranges + imports, exports, and TODO/FIXME warnings. |
| `get_symbol` | Full source code of a specific symbol + surrounding file context (imports, siblings, warnings). |
### Dependency graph (no API calls)
| Tool | Description |
|------|-------------|
| `get_dependencies` | Files that a given file imports. Supports `depth` parameter for transitive traversal. |
| `get_dependents` | Files that import a given file (reverse dependencies). |
| `get_blast_radius` | BFS impact analysis: finds all files transitively affected by changes to a file. Returns severity (LOW/MEDIUM/HIGH) and files grouped by depth. |
### Architecture & documentation (no API calls, v4.4)
| Tool | Description |
|------|-------------|
| `get_boundary_violations` | Returns architecture layer violations detected at index time. Rules are loaded from `stellaris.boundaries.json` at project root (`{ "deny": [{ "from": "src/ui/**", "to": "src/db/**", "reason": "..." }] }`). Glob-style patterns. Zero runtime overhead — detection happens during indexing. |
| `find_doc_references` | Find markdown/spec files that reference a code symbol or file (via `` `backtick` `` identifiers). Useful before renaming or deleting documented code. |
### Usage tracking (no API calls)
| Tool | Description |
|------|-------------|
| `usage_stats` | Token consumption and estimated API cost. Group by `model`, `project`, `day`, `cache`, `anomaly`, `category`, `mcp`, or `core_tool`. |
| `usage_dashboard` | Launches a local web dashboard (port 8090) with interactive charts, session breakdown, cache analytics, and Breakdown tab. |
| `usage_breakdown` | Structured Markdown report: task category breakdown, MCP server breakdown, core tool breakdown. Accepts `period` parameter. |
## MCP Prompts
Type `/nova` in Claude Code to access guided workflows:
| Prompt | Description |
|--------|-------------|
| `/nova_explore` | Full codebase walkthrough — file_tree → search → outline → symbol |
| `/nova_find` | Locate how a feature is implemented (semantic → drill-down) |
| `/nova_file` | Deep-dive into a specific file — outline + key symbols |
| `/nova_review` | Review recently changed files and assess their blast radius |
| `/nova_usage` | Show token consumption stats and open the interactive usage dashboard |
## Context-aware design
A common pitfall with code search tools is returning results that are **too precise** — the LLM gets the exact function it asked for, but misses the surrounding context needed to make safe decisions (imports, sibling functions, TODO warnings).
Stellaris addresses this with **automatic context enrichment**:
- **`get_symbol`** returns the requested source code **plus** file-level context by default:
- **Imports** — so the LLM knows where dependencies come from
- **Sibling symbols** — names and line ranges of other functions/classes in the same file, preventing duplications and revealing patterns
- **Warnings** — TODO, FIXME, HACK, NOTE, @deprecated comments found anywhere in the file
- **`get_file_outline`** returns symbol names **plus** the file's imports and exports, so the LLM understands the dependency graph before diving into code.
This adds ~100-200 tokens of "useful noise" per call — far cheaper than reading the entire file (~800-2000 tokens), while preventing blind refactoring errors.
On files ≤150 lines, `get_symbol` omits `file_context` (Read the small file instead). The `context` parameter can be set to `false` if you only need the raw source.
### Example `get_symbol` response
```json
{
"file": "src/indexer/chunker.ts",
"symbol": "chunkCodeAST",
"lines": "299-380",
"source": "function chunkCodeAST(content, file) { ... }",
"file_context": {
"imports": ["node:crypto", "tree-sitter", "../config/defaults.js"],
"exports": ["chunkFile", "parseFileSymbols", "extractFileContext"],
"siblings": [
"function extractImports (261-285)",
"function chunkMarkdown (382-429)",
"function chunkCodeFallback (431-465)"
],
"warnings": ["L42: TODO handle edge case for empty files"]
}
}
```
## Recommended workflow
1. **`reindex`** — index the project for the first time (builds vector, FTS, and graph indexes)
2. **`get_file_tree`** — discover the project structure
3. **Find code** — intent → `search_code` (hybrid). Known identifier definition → `search_code` (FTS). Occurrences of a known name → grep/rg. Optional `path` to search another already-indexed project.
4. **`get_file_outline`** — view symbols + imports/exports in a matched **large** file (Read small files directly)
5. **`get_symbol`** — one body from a large file; follow the `next:` hint. Do not Read the whole file.
Or use `/nova_explore` / the [stellaris-explore skill](skills/stellaris-explore/SKILL.md).
**Impact analysis workflow:**
1. **`get_dependents`** — find who imports a file you're about to change
2. **`get_blast_radius`** — get full transitive impact before making changes
3. **`get_dependencies`** — understand what a file relies on
Steps 2, 4, 5, and all graph tools consume **zero API tokens**.
After the first `reindex`, a `.stellarisrc` file is created in the project root with `auto_index=true`. Subsequent server startups will automatically run incremental indexing (only changed files).
### Auto-reindex hook (Grok + Claude Code)
`auto_index=true` only runs at **MCP startup**. While a session is open, new/edited files are indexed by a `PostToolUse` hook.
**Grok (automatic).** `npm install` installs `~/.grok/hooks/stellaris-reindex.json` when `~/.grok` exists and Node 22 (`nova-node`) is available. It fires after `write` / `search_replace` (and Claude aliases `Write` / `Edit`), reads the path from hook stdin, and reindexes that file. Re-run:
```bash
npm run install-hooks
```
Then start a new Grok session (or `/hooks-list` to confirm `stellaris-reindex`). The hook must run under Node 22 — never PATH Node 26 — because it loads `better-sqlite3`.
**Claude Code (manual).** Add this to `~/.claude/settings.json` (replace the path):
```json
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "node \"/path/to/stellaris-code-search/scripts/reindex-file.mjs\" \"$file_path\" 2>&1 || true",
"timeout": 30
}]
}]
}
}
```
## Installation
```bash
git clone https://github.com/GDM-Pixel/stellaris-code-search.git
cd stellaris-code-search
npm install
npm run build
```
**Node 22 only (ABI 127).** Grok / Nova launch this server with `nova-node`, not PATH `node`. This repo has a `mise.toml` pin. Do not `npm rebuild better-sqlite3` under Node 24/26 — it breaks the MCP for every project until you reinstall with Node 22.
```bash
# if PATH node is not 22:
/home/charles/.local/bin/nova-node "$(command -v npm)" install
npm run doctor # must print ABI 127
```
## Configuration
### Environment variables
| Variable | Required | Description |
|----------|----------|-------------|
| `OPENAI_API_KEY` | For OpenAI provider (default) | API key for `text-embedding-3-small` |
| `EMBEDDING_PROVIDER` | No (default: `openai`) | `openai` \| `voyage` \| `ollama` |
| `VOYAGE_API_KEY` | For Voyage provider | API key for `voyage-code-3` embeddings |
| `VOYAGE_MODEL` | No (default: `voyage-code-3`) | Override Voyage embedding model |
| `OLLAMA_HOST` | No (default: `http://localhost:11434`) | Ollama base URL |
| `OLLAMA_MODEL` | No (default: `nomic-embed-text`) | Ollama embedding model |
| `RERANK_PROVIDER` | No (default: `off`) | `off` \| `voyage` \| `cohere` — enables re-ranking |
| `VOYAGE_RERANK_MODEL` | No (default: `rerank-2`) | Voyage re-rank model |
| `COHERE_API_KEY` | For Cohere re-ranker | API key for `rerank-v3.5` |
| `STELLARIS_CONTEXT_WINDOW` | No (default: `128000`) | Calling LLM's context window in tokens. Drives tier-based result limits and truncation thresholds: small (<50K), medium (50–150K), large (150–500K), massive (>500K). |
Without any embedding API key (and no Ollama), the server starts normally — `get_file_tree`, `get_file_outline`, and `get_symbol` work without it.
#### Switching embedding providers
If you change `EMBEDDING_PROVIDER` on an existing index, Stellaris will refuse to run an incremental reindex (to avoid silently corrupting the vector store). Run:
```bash
# Force-rebuild the index with the new provider
reindex force=true
```
This deletes the old LanceDB table and `meta.json`, then rebuilds from scratch.
### `.vectorconfig.json` (optional)
Place at the root of the project to index:
```json
{
"include": ["src/**", "packages/**", "docs/**"],
"exclude": ["node_modules/**", "dist/**", "**/*.test.ts"],
"chunkStrategy": "ast"
}
```
### `stellaris.boundaries.json` (optional, v4.4)
Place at the project root to enforce architecture layer rules at index time. Any `depends_on` edge that matches a `deny` rule is flagged as a boundary violation and surfaced through `get_boundary_violations`. Detection happens during `reindex` — there is no runtime cost.
```json
{
"deny": [
{
"name": "ui-never-imports-db",
"from": "src/ui/**",
"to": "src/db/**",
"reason": "UI layer must go through services, not DB directly"
},
{
"from": "src/domain/**",
"to": "src/infrastructure/**",
"reason": "Hexagonal architecture: domain must stay infrastructure-agnostic"
}
]
}
```
Glob syntax: `**` matches any depth, `*` matches one path segment, `?` matches one character. Paths are relative to project root, forward slashes.
### `.stellarisrc` (auto-generated)
Created automatically after the first successful `reindex`. Controls auto-indexing and embedding configuration.
```
# Stellaris Code Search configuration
auto_index=true
# Embedding provider (openai | voyage | ollama) — default: openai
# embedding_provider=voyage
# embedding_model=voyage-code-3
# Re-ranking (off | voyage | cohere) — default: off
# rerank_provider=voyage
# Import-alias overrides (safety net if auto-detection from
# tsconfig paths / vite resolve.alias fails). Path is relative
# to the project root.
# alias.@=src
# alias.#utils=src/lib/utils
```
You can toggle `auto_index` via the `reindex` tool (`enable_auto_index: false`) or edit the file manually.
**Import alias resolution.** The dependency graph auto-detects path aliases from the `tsconfig.json`/`jsconfig.json` nearest each source file (following `extends`, including monorepo subdirectories), falling back to `vite.config.*` `resolve.alias`. The `@/` and `~/` conventions resolve to the nearest `src/` automatically. Use the `alias.<name>=<path>` lines above only as an override when auto-detection can't find your config.
### `.vectorignore` (optional)
Same syntax as `.gitignore`, to exclude files from indexing.
## Security
Stellaris **never indexes sensitive files**. Two layers of protection ensure secrets are never sent to OpenAI:
1. **Glob exclusions** (`DEFAULT_EXCLUDE`) — files matching these patterns are never scanned:
- `.env*`, `secrets.*`, `credentials.*`
- `*.pem`, `*.key`, `*.cert`, `*.p12`, `*.pfx`, `*.keystore`
2. **Ignore filter** (defense in depth) — same patterns applied via the `ignore` library during file scanning, as a second safety net.
Additionally, `.gitignore` and `.vectorignore` rules are always respected.
## Claude Desktop integration
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"stellaris-mcp": {
"command": "node",
"args": ["/path/to/stellaris-code-search/dist/index.js"],
"env": {
"OPENAI_API_KEY": "sk-..."
}
}
}
}
```
To use Voyage embeddings instead of OpenAI:
```json
{
"mcpServers": {
"stellaris-mcp": {
"command": "node",
"args": ["/path/to/stellaris-code-search/dist/index.js"],
"env": {
"EMBEDDING_PROVIDER": "voyage",
"VOYAGE_API_KEY": "pa-...",
"RERANK_PROVIDER": "voyage"
}
}
}
}
```
To use Ollama (fully local, no API key needed):
```json
{
"mcpServers": {
"stellaris-mcp": {
"command": "node",
"args": ["/path/to/stellaris-code-search/dist/index.js"],
"env": {
"EMBEDDING_PROVIDER": "ollama",
"OLLAMA_MODEL": "nomic-embed-text"
}
}
}
}
```
## Supported languages & formats
| Language / Format | Extensions | Parsing | Symbol types |
|-------------------|-----------|---------|--------------|
| TypeScript | `.ts` | tree-sitter (AST) | function, component, hook, class, type |
| TSX | `.tsx` | tree-sitter (AST) | function, component, hook, class, type |
| JavaScript | `.js` | tree-sitter (AST) | function, component, class |
| JSX | `.jsx` | tree-sitter (AST) | function, component, class |
| Python | `.py` | tree-sitter (AST) | function, class |
| Go | `.go` | tree-sitter (AST) | function, method, type |
| Rust | `.rs` | tree-sitter (AST) | function, struct, impl, trait, type |
| PHP | `.php` | tree-sitter (AST) | function, class, type |
| **Java** | `.java` | **tree-sitter (AST)** | class, interface, enum |
| **Ruby** | `.rb` | **tree-sitter (AST)** | class, module, method |
| HTML | `.html` | tree-sitter (AST) | element |
| CSS | `.css` | tree-sitter (AST) | rule |
| Astro | `.astro` | fallback (chunked) | module |
| Vue | `.vue` | fallback (chunked) | module |
| Svelte | `.svelte` | fallback (chunked) | module |
| SCSS / Less | `.scss`, `.less` | fallback (chunked) | module |
| JSON | `.json` | fallback (chunked) | module |
| YAML | `.yaml`, `.yml` | fallback (chunked) | module |
| SQL | `.sql` | fallback (chunked) | module |
| GraphQL | `.graphql`, `.gql` | fallback (chunked) | module |
| Prisma | `.prisma` | fallback (chunked) | module |
| TOML | `.toml` | fallback (chunked) | module |
| Markdown | `.md`, `.mdx` | heading-based | doc_section |
## Architecture
```
src/
index.ts # MCP entry point, tool + prompt registration
startup.ts # Auto-indexing on startup (reads .stellarisrc)
prompts.ts # MCP Prompts definitions (nova_explore, nova_find, nova_usage, ...)
config/
defaults.ts # Extensions, chunking settings, LanceDB config
loader.ts # .vectorconfig.json loader
stellarisrc.ts # .stellarisrc reader/writer
indexer/
scanner.ts # File scanning (.gitignore, .vectorignore)
chunker.ts # Multi-language AST parsing + symbol extraction
embedder.ts # Embedding factory (provider-agnostic)
hasher.ts # SHA-256 hashing + _index_config sentinel
providers/
base.ts # EmbeddingProvider interface + retry helper
openai.ts # OpenAI provider (text-embedding-3-small)
voyage.ts # Voyage AI provider (voyage-code-3)
ollama.ts # Ollama provider (nomic-embed-text, local)
store/
lancedb.ts # LanceDB vector storage (dynamic dims)
fts.ts # SQLite FTS5 full-text index
search/
route.ts # Query classifier: identifier/exact → FTS, intent → hybrid
hybrid.ts # RRF fusion of vector + FTS results + optional rerank
reranker.ts # Voyage / Cohere re-ranking post-RRF
graph/
resolver.ts # Import string → real file path resolution
store.ts # SQLite dependency graph (graph.db)
blast.ts # BFS blast radius + dependency chain
tools/
searchCode.ts # search_code tool (routed FTS / hybrid)
searchDocs.ts # search_docs tool (hybrid)
reindex.ts # reindex + reindex_file tools
getFileTree.ts # get_file_tree tool
getFileOutline.ts # get_file_outline tool
getSymbol.ts # get_symbol tool
getDependencies.ts # get_dependencies tool
getDependents.ts # get_dependents tool
getBlastRadius.ts # get_blast_radius tool
usageStats.ts # usage_stats tool (group_by: model/project/day/cache/anomaly/category/mcp/core_tool)
usageDashboard.ts # usage_dashboard tool + HTTP server
usageBreakdown.ts # usage_breakdown tool (Markdown report)
usage/
scanner.ts # JSONL scanner — global dedup by message.id, MCP/core split, classifier
store.ts # SQLite schema: turns, sessions, processed_files + v3.9 columns
pricing.ts # Per-model pricing table (April 2026)
classifier.ts # 13-category heuristic classifier (bilingual FR+EN)
dashboard.ts # Interactive HTML dashboard renderer (5 tabs incl. Breakdown)
indexer/
integrity.ts # Startup integrity check: orphan purge + stale meta cleanup
scripts/
reindex-file.mjs # Hook script for auto-reindex after Write/Edit
```
## Storage
The index is stored in `.vectors/` at the project root:
- `.vectors/lancedb/` — LanceDB vector database (embeddings)
- `.vectors/fts.db` — SQLite FTS5 full-text index
- `.vectors/graph.db` — SQLite dependency graph
- `.vectors/meta.json` — file meta-index (hashes, chunk IDs, timestamps)
This directory is automatically excluded from scanning.
Usage data is stored globally in `~/.claude/usage.db` (SQLite). Data older than 180 days is automatically purged at startup. The dashboard shows the last 90 days.
At every startup, an **integrity check** runs automatically:
- Orphaned chunks (in LanceDB/FTS/graph but absent from `meta.json`) are purged from all 3 stores
- Stale `meta.json` entries (source file deleted from disk) are removed so the next reindex handles them correctly
## Development
```bash
npm run dev # Run with tsx (hot reload)
npm run build # Compile TypeScript
npm run watch # Watch mode compilation
```
## License
[MIT](LICENSE)
TDQS
Scored across 30 tools
Each tool has a clear, distinct purpose with no overlaps. Dependency analysis tools (get_dependencies, get_dependents, get_most_coupled, etc.) are precisely differentiated, and the token-efficient exploration sequence (search_code -> get_file_outline -> get_file_folded -> get_symbol) is well-structured. Even closely related tools like db_schema and db_search serve different functions.
Most tools follow a verb_noun pattern with snake_case (e.g., get_dependencies, search_code). However, the verb choices vary (get, search, detect, find, simulate) and some names are compound (detect_significant_changes) or use adjectives (get_most_coupled), resulting in minor inconsistency.
With 30 tools, the server significantly exceeds the typical well-scoped range of 3-15. While each tool is justified, the set spans multiple domains (code analysis, database, usage monitoring) that could be split into separate servers. This large surface area may overwhelm agents.
The tool set comprehensively covers code analysis, dependency management, project health, documentation search, database introspection, and usage tracking. All essential operations for these domains are present, with no obvious gaps in the workflow.