SELF-HOSTED CODE INTELLIGENCE MCP SERVER · APACHE-2.0
Give your coding agent a memory of the codebase it can't get from grep.
Vaelor parses, graphs, and watches 16 languages, exposing 35+ MCP tools over one Apache-2.0 endpoint you run yourself.
- Languages
- 16
- MCP tools
- 35+
- License
- Apache-2.0
KNOWLEDGE GRAPH
What Vaelor remembers.
Your code as a property graph, indexed for agents. Every symbol, every call edge, every import, every test relationship. Queried in natural language via Cypher.
review_pr → understand
A review verdict that travels with the code.
review_pr stores one learning per changed symbol from the real GitHub review outcome. The next time anyone calls understand on that symbol, the closest prior learnings arrive with the answer. The memory lives next to the code, not in someone's head.
{
"repo": "acme/api",
"symbol": "auth.ValidateToken",
"review_outcome": "bad",
"flag": "policy:forbidden_import",
"note": "handler imports internal/db directly",
"pr_url": "github.com/acme/api/pull/412"
} {
"symbol": "auth.ValidateToken",
"callers": …,
"complexity": …,
"prior_learnings": [
{
"review_outcome": "bad",
"flag": "policy:forbidden_import",
"note": "handler imports internal/db directly"
}
]
} - WRITE APPROVE is stored as good, REQUEST_CHANGES as bad, anything else as neutral. A dry run records risk_level instead.
- RECALL understand looks up the nearest learnings for the symbol and attaches them as prior_learnings. No extra call.
- STORAGE PostgreSQL with pgvector, via LEARNINGS_DATABASE_URL or DATABASE_URL. With neither set, the loop returns nothing and logs a startup warning.
Illustrative records with a fictional repo; the field names are the real ones.
debug_investigate
From Prometheus alert
to file:function.
Seven-phase pipeline: metric spikes, Jaeger failed traces, symbol resolution,
callgraph walks upstream and downstream, log excerpts, deployed-binary drift,
LLM fusion. Returns ranked file:function hypotheses with body excerpts,
so an incident resolves to code, not just a dashboard alert.
Example output, illustrative. DRIFT runs when the investigation is given a host; it diffs image tags pinned in source against the containers actually running.
semantic_search
Find by concept.
Not by keyword.
Hybrid retrieval: BM25F lexical scoring, pgvector 768-dim embeddings, one-hop AGE graph expansion, cross-encoder reranker. Finds what grep can't describe.
- auth.ValidateToken auth/token.go:142 ctx.Deadline() exceeded before JWKS fetch completed — timeout not propagated
- middleware.AuthChain auth/middleware.go:88 calls ValidateToken without context timeout guard
- jwks.FetchPublicKey auth/jwks.go:61 HTTP client uses default transport, no deadline
- server.handleAuth server/routes.go:207 auth handler called with request context
- TestValidateToken_Timeout auth/token_test.go:89 test exists but uses 30s timeout — too lenient
- config.AuthTimeout config/auth.go:34 timeout constant: 0 — not set
- cache.TokenStore cache/tokens.go:19 in-memory LRU, TTL-bound
- errors.ErrAuthFailed errors/auth.go:8 sentinel error used in 4 callers
- retry.WithBackoff retry/retry.go:44 exponential backoff, used by JWKS fetcher
- otel.RecordError telemetry/spans.go:91 helper for span error annotation
Cypher via
natural language.
Apache AGE property graph on PostgreSQL. Every symbol, call edge, import, route handler, test relationship. Query the graph from plain English: Vaelor translates to Cypher and returns structured results.
MATCH (caller:Symbol)-[:CALLS]->(fn:Symbol)
WHERE fn.name = 'ValidateToken'
AND fn.package = 'auth'
OPTIONAL MATCH (fn)-[:TESTED_BY]->(test:Symbol)
RETURN caller.name,
caller.file,
caller.line,
test.name AS test_coverage
ORDER BY caller.file; dataflow_analyze + rewrite
Taint tracking.
Structural codemods.
IL/CFG analysis traces values from source to sink. Detects dead stores, unescaped user input reaching SQL or command execution. Rewrite applies AST search-replace patterns across all 16 languages with wildcard matching, survives multi-line code that regex can't touch.
return errors.New(msg)
return fmt.Errorf("%s: %w", msg, err)
return nil, errors.New(queryErr)
return nil, fmt.Errorf("query: %w", queryErr)
return errors.New(cacheErr)
return fmt.Errorf("cache: %w", cacheErr)
... +244 more errors.New($MSG) → fmt.Errorf("%s: %w", $MSG, err) positioning
Where the graph lives.
grep sees text. LSP sees one file's types. Sourcegraph indexes repos for humans. Cursor's indexing is locked inside one editor. Vaelor ships the same structural depth as a portable, MCP-native endpoint any agent can call — Claude Code, Cursor, Windsurf, Aider, Cline. No editor lock-in, no per-session rebuild.
quickstart
Five minutes from clone
to first MCP call.
Just an LLM endpoint and you have repo analysis, call tracing, structural rewrite, dataflow, and PR review. Add Postgres + AGE for code_graph; ox-embed-server + pgvector for semantic_search and code_research reranking; ox-codes for stricter dead_code + full code_health grades. Redis is just an L2 cache. All companions are open source.
$ docker build -t vaelor .
Building vaelor ... done
$ docker run -p 8897:8897 \
-e LLM_API_BASE=http://host.docker.internal:8317/v1 \
-e LLM_API_KEY=your-key \
vaelor
MCP server listening on :8897
$ claude mcp add -s user -t http vaelor http://127.0.0.1:8897/mcp
→ registered — Claude Code can now call vaelor FAQ
Questions you're already asking.
Why not just grep or ripgrep?
grep and ripgrep find the function. They don't tell an agent what else breaks when it changes, and they don't remember your last review or your last production incident. Vaelor parses 16 languages with tree-sitter, builds a call graph (type-aware on Go and, via SCIP, on TypeScript, Python, Java and Rust), and exposes 35+ MCP tools — understand, impact_analysis, call_trace, review_pr, debug_investigate — that surface blast radius, dead code, and prior review verdicts. The structural depth grep can't reach is the whole point.
Why not an LSP, or Sourcegraph?
An LSP gives you type-aware navigation inside one file in one editor. Sourcegraph indexes code for humans — full-text and symbols across an org, built for humans first, with no call graph of what runs in production. Vaelor sits where neither does: structural depth (AST + call graph + type resolution) at multi-repo scale, exposed as a portable MCP endpoint any agent can call. It also adds what neither has: an Apache AGE knowledge graph carrying PageRank, community, and surprise scores, and a runtime observability bridge that correlates a Prometheus alert and a Jaeger trace back to the function that caused it.
Why not Cursor's built-in indexing?
Cursor's indexing is structural-ish, but it only serves Cursor — the index, the embeddings, and the chunk graph live inside one editor and one vendor's cloud. Vaelor is MCP-native, so the same endpoint serves Claude Code, Cursor, Windsurf, Aider, Cline, or any client implementing the Model Context Protocol. It's also self-hosted and Apache-2.0: you run the binary, you own the graph, the code never leaves your infrastructure. No editor lock-in, no vendor index.
Isn't running Postgres + Apache AGE + pgvector + an embedder a lot of ops?
Day one is two commands: docker build and docker run with an OpenAI-compatible LLM endpoint. That gives you call_trace, understand, impact_analysis, code_search, dead_code, code_health, repo_analyze, and the structural tools that don't need a graph or embeddings. Add PostgreSQL with Apache AGE (DATABASE_URL) to enable code_graph and the persistent PageRank/community/surprise signals. Add pgvector + an embedder (EMBED_URL) to enable semantic_search and code_research reranking. Redis is optional L2 cache. Each sidecar unlocks a specific tool family — nothing is required for the core, and the core is most of the tools.
What does it work with?
Any MCP client. Vaelor speaks Streamable HTTP over the Model Context Protocol, so you register one URL and it works with Claude Code, Cursor, Windsurf, Aider, Cline, and any future client that implements MCP. One server, every agent — no per-editor plugin to maintain.
Which languages does it support?
16: Go, Python, TypeScript/JavaScript, Rust, Java, C, C++, Ruby, C#, PHP, Svelte, Astro, Vue, Kotlin, Swift, and HTML. Tree-sitter AST parsing across all of them. Call resolution is type-aware on Go via go/types, and on TypeScript, JavaScript, Python, Java and Rust through SCIP indexers (bundled in the Docker image). Other languages fall back to tree-sitter call edges.
What's the licence, and is there a hosted version?
The engine is Apache-2.0. Self-host it, fork it, run it inside your firewall. There is no hosted SaaS and no signup: you run the binary, and self-hosting is the only path. Krolik (krolik.tools) is the same engine with its own front door, also self-hosted.
Do I need an LLM API key to try it?
No. Most tools run without LLM_API_KEY — they skip only the narrative and ranking layer. call_trace, impact_analysis, dead_code, code_search, and the structural tools return their raw structural results. Only the natural-language parts, such as code_graph queries and repo_search, need the key. Add an OpenAI-compatible endpoint (LiteLLM, CLIProxyAPI, or any wrapper) to get the LLM narratives and the reranker on code_research. The key is a capability unlock, not a gate.