EMBEDDING_MODELS — Embedding models in selection order; the current runtime uses the first provider/model entry; empty = local ONNX
LOCAL_EMBEDDING_MODEL — Built-in fastretrieval model ID or local directory with fastretrieval-manifest.json
LOCAL_RERANK_MODEL — Fastretrieval TextCrossEncoder model ID; empty = reranking disabled
LOCAL_EMBEDDING_DIM — Required positive output dimension for an external local embedding ID without a manifest
LOCAL_EMBEDDING_MODEL_FILE — ONNX file path inside a manifest-backed local embedding directory
LOCAL_EMBEDDING_POOLING — Explicit pooling for an external local embedding ID without a manifest
LOCAL_EMBEDDING_NORMALIZE — Explicit L2 normalization for an external local embedding ID without a manifest
HULL_EMBED_API_KEY — API key for the [models.embed] cell transport (OpenRouter pre-wired default); host-injected, overrides the cell api_key
HULL_CHAT_API_KEY — API key for the [models.chat] cell transport (OpenRouter pre-wired default); host-injected, enables LLM summaries when the cell model is set
EMBEDDING_BACKEND — DEPRECATED (honored one release): embedding backend override 'local' or 'cloud'. Use EMBEDDING_MODELS instead.
Claude Code ไม่มีลิงก์ติดตั้งสำหรับคำสั่งแบบโลคัล ให้คัดลอกคำสั่งด้านล่างแล้วรันในเทอร์มินัล
Open-source MCP server for AI agents: web search, content extraction, and lib...
MCP
An MCP server that parses your codebase with Tree-sitter, builds a structural graph of functions/classes/imports, and gives Claude (or any MCP client) precise context so it reads only what matters instead of the whole tree. Semantic search runs through the local ONNX model registry from fastretrieval by default (zero config, no API key), with an optional cloud embedding chain. Fork of code-review-graph with fixed multi-word search, qualified call resolution, dual-mode embeddings, output pagination, and production CI/CD.
v2.0 migration (BREAKING)
v2.0 adds temporal columns (valid_from_sha / valid_to_sha on every node + edge) and an opt-in security scanner. The schema migration is auto-applied on first GraphStore open, and a backup of the pre-2.0 DB is saved to <graph_db>.pre-2.0.bak so you can roll back. See BREAKING_CHANGES.md for the full schema-change list, behavior changes, environment requirements, and the downgrade procedure (CRG_DOWNGRADE_TO_1_X=1 uv run crg).
For OMP and other local coding harnesses, the primary surface is the package CLI
plus the bundled skills/ workflows. The skills invoke the CLI directly and do
not require an MCP server mapping.
# Run without a persistent install (short `crg` script; PyPI package name stays
# better-code-review-graph, so `uvx` needs the explicit --from form)
uvx --python 3.13 --from better-code-review-graph crg graph build --full-rebuild \
--repo-root /path/to/repo
uvx --python 3.13 --from better-code-review-graph crg graph stats \
--repo-root /path/to/repo
# Or install the console scripts (installs both `crg` and the legacy long name)
pip install better-code-review-graph
crg query search --search-query "authentication" \
--repo-root /path/to/repo
The optional Semgrep engine for deeper security scans is a separate extra:
pip install 'better-code-review-graph[security]'
MCP stdio remains a secondary protocol adapter for clients that require it:
Two ways to run the server, plus the surfaces to consume it.
Dev: uv, no auth (loopback only)
# instance config lives at $CRG_CONFIG_DIR or ~/.crg (same schema as hull's
# config.toml); a default no-auth template is fine for local dev
uv run better-code-review-graph server start
# -> http://127.0.0.1:8000/mcp (no-auth is refused on any off-loopback bind)
Always-on: docker (token auth)
cp docker-config/config.example.toml docker-config/config.toml
# mint a token + hash (hull CLI ships with crg's dependency tree):
# TOKEN=$(python -c "import secrets; print(secrets.token_urlsafe(32))")
# hull token hash "$TOKEN" # paste output into token_hash in config.toml
docker compose up -d
The compose file builds the Dockerfile's http target (image crg:http),
publishes the container on host loopback only
(127.0.0.1:${CRG_PORT:-8772}:8080), and mounts
./docker-config/config.toml read-only at the container's config dir
(CRG_CONFIG_DIR=/data/config); graph state persists in the crg-data
named volume. All state stays on your machine.
Each task (embed / rerank / chat / jev_score) resolves its own OpenAI-spec
provider cell from [models.<task>] in the instance config: an independent
base_url + api_key + model. Cloud or local is purely a config choice —
point base_url at OpenRouter, a vendor, or your own local gateway
(e.g. http://host.docker.internal:11434/v1). API keys are host-only
material: keep them in config.toml (gitignored under docker-config/) or
supply via HULL_<TASK>_API_KEY env; they are never end-user supplied.
Local-first boundary
CRG is local-first for coding workflows:
CLI and bundled Skills are the primary surfaces for graph build/query,
impact analysis, review context, security scans, and repository onboarding.
MCP stdio is the secondary protocol adapter over the same local domain
services; it does not maintain a separate graph implementation.
Graph state stays in <repo>/.crg/graph.db unless an explicit
multi-user/self-host configuration selects another data directory.
PyPI, CI, security scanning, GitHub releases, and eligible stable MCP Registry
publication remain active. Historical public OCI tags are retained, but new
public Docker Hub/GHCR images are no longer published.
CRG has no hosted Cloudflare runtime in the target topology.
Smithery
The repo ships a smithery.yaml so the server can be built and
run through Smithery. It deploys over stdio and needs
no startup configuration -- the config schema is empty, and any optional cloud
embedding/summary keys are supplied at runtime through the server's own config
flow (see Configuration below). The launch command is the same
uvx invocation as a local install:
Everything works out of the box with zero configuration -- semantic search
uses the local ONNX registry from fastretrieval
(Qwen3-Embedding-0.6B is the current built-in reference entry, ~570 MB
downloaded on first graph embed). This reference entry is not a Qwen-only
boundary: any built-in registry ID or valid non-Qwen artifact manifest follows
the same resolver. All environment variables below are optional and only needed
for cloud embeddings, LLM summaries, or an explicit BYO local artifact.
Model selection
Embeddings select the first provider/model entry in EMBEDDING_MODELS; later
entries are retained as configuration but are not runtime fallbacks. The summary
model comes from the model field of the [models.chat] cell (an unconfigured
cell disables summaries). Transport always comes from the per-task
[models.<task>] cell (OpenRouter pre-wired default) — model-name prefixes
only select wire details, never keys.
Variable
Purpose
Empty (default)
EMBEDDING_MODELS
Cloud embedding selection; the first entry is active
Local fastretrieval registry
[models.chat] cell model
Completion model selection for graph(action="summarize")
Summaries disabled
Cohere embed-v4.0 requests and stores 1024 dimensions; other backends retain
768-dimensional storage. CRG never slices, pads, or silently accepts a different
provider width. The embedding row's model and byte width must match before reuse.
Run graph(action="embed") after changing models or upgrading an old 768-wide
Cohere index. Searches reject incompatible widths before a provider call; graph
nodes are retained and re-embedding replaces only stale vectors.
Provider API keys
The key lives in the per-task cell's api_key (host-only; see the table below).
Keys alone never select models: an empty embedding chain stays local, and an empty summary
chain stays disabled. A configured cloud error does not fall back to local or
another provider. Summarizers require a chat-completion model.
Task
Config cell
Notes
Embedding
[models.embed] in the instance config (base_url + api_key + model), or HULL_EMBED_API_KEY env
Plain OpenAI-spec HTTP through hull-core; OpenRouter pre-wired default. EMBEDDING_MODELS picks the model name(s) — keys never select a model. Model prefixes (cohere/…, jina_ai/…, gemini/…) select wire details only (e.g. Cohere input_type); there are no per-vendor API keys. Cohere embed-4.0 direct is the documented opt-in exception (spec 2026-09-26 §9).
Summarizer
[models.chat] cell (or HULL_CHAT_API_KEY)
Requires a chat-completion model; OpenRouter default.
Vendor-specific key env vars (JINA_AI_API_KEY, GEMINI_API_KEY,
OPENAI_API_KEY, GOOGLE_VERTEX_EXPRESS_API_KEY, …) are not read by
crg; the removed per-vendor dispatch lived in the pre-de-host stack.
Advanced
Variable
Purpose
EMBEDDING_MODELS
Comma-separated embedding model chain; the first entry is the cloud model (host-owned; keys never select a model). Legacy EMBEDDING_MODEL is honored with a deprecation warning until the next release.
DISABLE_LOCAL_EMBED
Skip the local ONNX download; embedding is unavailable unless a cloud chain is configured
LOCAL_RERANK_MODEL
Fastretrieval TextCrossEncoder model ID for bounded semantic reranking
LOCAL_EMBEDDING_DIM
Required dimension for an external model ID without a manifest
LOCAL_EMBEDDING_MODEL_FILE
ONNX file path inside a manifest-backed artifact directory
LOCAL_EMBEDDING_POOLING
Explicit pooling for an external ID without a manifest: CLS, MEAN, LAST_TOKEN, or DISABLED
LOCAL_EMBEDDING_NORMALIZE
Explicit L2 normalization for an external ID without a manifest
CRG_DATA_DIR
Override the per-user data directory (default ~/.crg) used for per-user graphs and credentials in HTTP multi-user mode
EMBEDDING_BACKEND / EMBEDDING_MODEL
Deprecated singular vars, honored one release with a warning -- migrate to EMBEDDING_MODELS. The pre-de-host summary-model and base-URL env vars are no longer read; model + transport live in the [models.*] cells
When LOCAL_RERANK_MODEL is configured, semantic vector search retrieves a
bounded candidate pool of min(max(limit * 4, limit), 100) rows, applies the
existing kind, repo, and live-row filters, then reranks that pool and returns
at most limit rows. The response uses search_mode="semantic_reranked" and
adds rerank_score while preserving similarity_score. Blank keeps the
existing limit * 2 vector path and search_mode="semantic". Configured
reranker failures return an explicit error; CRG does not silently fall back to
vector or keyword results. Keyword searches, including as_of snapshots, do
not invoke the reranker.
Example -- cloud embeddings + summaries
~/.crg/config.toml (write by hand, or start from hull config init which
pre-wires OpenRouter defaults):
[models.embed]
base_url = "https://openrouter.ai/api/v1"
api_key = "sk-or-..."
model = "cohere/embed-v4.0"
[models.chat]
base_url = "https://openrouter.ai/api/v1"
api_key = "sk-or-..."
model = "minimax/minimax-m3:free"
Equivalent env surface for the MCP server entry (keys override the cells;
model names still come from EMBEDDING_MODELS / the model field):
Cohere embedding is paid. Authorize a bounded budget before a live index/query;
the Minimax-free completion choice does not make embeddings free. Model-name
prefixes (cohere/…, openrouter/…) select wire details only — transport and
credentials come from the [models.*] cells, which the host owns.
CRG currently has no cloud rerank call: LOCAL_RERANK_MODEL is its only
reranking path. Setting RERANK_MODELS or RERANK_API_BASE does not enable one.
Tools
Six tools, each grouping related actions to keep the tool surface small.
Returns complete documentation for each tool. Use when the compressed
descriptions above are insufficient.
CLI
The package installs two console scripts: crg (primary) and
crg (legacy long name). Running either with no
arguments starts the MCP server over stdio; a leading positional argument
routes to a local CLI subcommand that calls the same domain services used by
the MCP adapter. Run them directly after pip install, or without a
persistent install via uvx --python 3.13 --from better-code-review-graph crg ....
# Start the MCP server over stdio (default -- no subcommand)
crg
# Build, inspect, and embed the local graph
crg graph build
crg graph stats
crg graph embed
# Query relationships and impact
crg query query \
--pattern callers_of --target "path/to/module.py::function"
crg query search --search-query "authentication"
crg query impact --changed-files src/app.py
# Produce review context and run a local security scan
crg review context --base HEAD~1
crg security scan --engine heuristic
Command
Description
graph build
Full or incremental graph build. --full-rebuild re-parses every file; --base <ref> sets the incremental diff ref; --repo-root <path> overrides auto-detection.
graph embed
Compute vector embeddings using local ONNX or the configured cloud chain.
This plugin implements TC-Local (machine-bound, single trust principal). See the mcp-core trust model for full classification.
Mode
Graph DB
Cloud credentials
Who can read your data?
stdio (default)
<repo>/.crg/graph.db (git-ignored)
~/.crg/config.toml (AES-GCM, machine-bound key)
Only your OS user
HTTP self-host (multi-user)
Per-user ~/.crg/subs/<sub>/graph.db
Per-user ~/.crg/subs/<sub>/config.json
Only the authenticated user
Migration & changelog
Graph, security scan cache, and suppression state now use the package-owned
.crg/ directory. Run graph(action="build", full_rebuild=true)
once after upgrading, followed by graph(action="embed") if semantic search is
needed. The old .better-code-review-graph/ state directory plus the ambiguous
.code-review-graph/ and .code-review-graph.db paths and their SQLite
sidecars are left untouched, not migrated. Review and reapply any desired
suppression rules explicitly.
The v2.0 release added temporal columns (valid_from_sha / valid_to_sha
on every node and edge) plus an opt-in security scanner. The schema migration
is auto-applied on first GraphStore open, and a backup of the pre-2.0 DB is
written to <graph_db>.pre-2.0.bak. To downgrade and restore it: