English | Turkce
A code graph for AI coding agents that keeps up with your edits — and tells you when it is not watching.
CCM parses your project with tree-sitter (13 languages), keeps a call and import graph plus an optional semantic index on disk, and serves them to agents such as Claude Code, Codex and Cursor through MCP. The recommended agent entry point is
context: give it a symbol,path:line, or a natural- language coding task and CCM chooses structural graph lookup or hybrid search server-side. On Django 5.1 (6,670 files) a saved change shows up in graph results after a median of about 0.6 s (measurement: release build, embedder off). Query responses start with the index state (fresh,stale · …while a registered change is pending, orauto-refresh off · indexed Xs ago), and a failed re-index never replaces the last good graph. In the pre-registered L1 freshness benchmark (one-function edits in Flask 3.0.3 and Django 5.1, probes at fixed times), every graph answer from 0.25 s after a save on matched the edit; in the sub-250 ms window before the watcher applies the event, 6 of 126 probes returned the pre-edit answer still labeledfresh, and 4 more a partial or empty one (benchmarks/).
Status: search quality has a 35-task pilot benchmark. In an LLM-free benchmark of 24 fixed questions on Flask and Django, the 0.4.0 tools answer with 74% fewer response bytes and a third fewer calls than the 0.3 tools (token cost). A 2026-10-02 Claude Code pilot then showed the product-level gap: on three small/medium tasks the agent never called CCM, while merely exposing CCM increased input tokens. The active work therefore moves tool selection into the new one-call
contextentry point. That change has not yet been shown to reduce end-to-end agent tokens; the pilot and its negative result remain published underbenchmarks/results/agent/pilot-claude/.
Coding agents find code well with grep. What grep does not give them is
structure: who calls a function, what a change can break, which calls connect
two places. CCM answers those as graph queries (find_usages,
impact_of_change, trace_call_chain) and keeps the graph current while you
edit:
- Fresh after a save: a file watcher applies changes to the index the MCP server already holds in memory (median ~0.6 s on Django 5.1, graph only).
- Says when it is not watching: with auto-refresh off, responses say
auto-refresh off · indexed Xs ago. With auto-refresh on, the status line does not yet cover the sub-250 ms window above. - Never half-built: each index is built as a new generation and activated atomically; a failed or interrupted run leaves the previous graph serving.
Limits, stated up front. In Python files, calls and other uses of a name
(arguments, attribute access such as User.objects, type hints) are read from
the syntax tree and resolved through the file's imports (import,
from … import, relative imports, package re-exports including
from .x import *), self/cls/super() and class names;
a call on a receiver whose type is unknown is reported as a possible call to
at most five same-named definitions, and imports that leave the project produce
no edge. Rust files are resolved the same way through use paths (crate,
self, super, other workspace crates named in Cargo.toml, pub use
re-exports), inline and file modules, impl blocks and traits, including calls
inside macro arguments; a method call on a value whose type is unknown is a
possible call to at most five methods. In the other 11 languages call edges
are matched by name and labelled as inferred: a call binds to a definition in
the same file first, otherwise to the only definition elsewhere; several
same-file definitions produce edges marked ambiguous, and a name defined in
several other files produces no edge. How often this matches a type-aware tool
has not been measured yet. Other MCP servers
also build code graphs and refresh them automatically; CCM makes no claim of
being fresher or more accurate than them until that is measured
(benchmarks/).
- Two-Pass Indexing - Links function definitions to call sites
- Incremental Refresh - Re-indexes only added, modified, renamed, or deleted files after the first run
- Deep Traversal - Ask "Who calls this?" and get the callers the graph knows (syntax-resolved for Python and Rust, name-matched elsewhere; see limits above)
- Rust-Powered - Single-binary CLI and MCP server; index and query timings
are published in
benchmarks/, not claimed here - Built-in Embeddings - Semantic search works out of the box: a pinned multilingual, code-trained embedding model runs inside the binary (no Ollama, no API key)
- Deterministic Embedding - One chunk per inference call on all physical cores, so a chunk's vector never depends on its neighbors (Ollama/OpenAI requests are batched)
- LanceDB - Millisecond-latency vector storage
- Tree-sitter - Robust AST for Rust, Python, TypeScript, JavaScript, Go, Java, Kotlin, C#, C, C++, Ruby, PHP, and Swift
- Binary Checksums - Release artifacts include
checksums.txtfor integrity - MCP Allowlist - Restrict project access with
CCM_ALLOWED_ROOTS - Safe Defaults - UTF-8-safe chunking, configurable timeouts, retries, and file-size limits
- Plug & Play - Installer configures Claude Code, Codex, Cursor, Claude Desktop, and Antigravity
- Explicit Indexing - Missing indexes fail fast and point to
index_project - Zero-Config - Auto-detects project root
# 1. Configure MCP for your AI editor
npx @senoldogann/context-manager install
# 2. Index your project
npx @senoldogann/context-manager index --path .After installation, verify the happy path with a clean smoke test:
# Check that the CLI responds
npx @senoldogann/context-manager query --text "src/main.rs:1"
# Start the MCP server directly
npx @senoldogann/context-manager mcpExpected outcomes:
- The wrapper downloads the correct binary for your OS and architecture
indexcreatesdata/ccm_dbinside the projectmcpstarts without JSON-RPC parse errors and waits on stdio
| Host | Status | Installation Path |
|---|---|---|
| Claude Code | Supported | claude mcp add-json --scope user (requires the claude CLI) |
| Codex | Supported | Atomic update of ~/.codex/config.toml |
| Cursor | Supported | ~/.cursor/mcp.json |
| Claude Desktop | Supported | Native desktop config |
| Antigravity | Supported | Native host config |
If your editor is not auto-detected, use the manual MCP config printed by the installer.
CCM ships a SKILL.md in both the source repository and the npm tarball. It teaches one default workflow: call context with the task or code question and let CCM choose graph/search operations server-side. The older structural tools remain documented as explicit compatibility and diagnostic primitives; the skill no longer recommends injecting a project map at session start.
Copy it into your agent's skill directory and it becomes a first-class tool reference:
# Install to your local agents skill directory
cp SKILL.md ~/.agents/skills/context-manager/SKILL.md# If local source builds fail, install protoc first.
# macOS: brew install protobuf
git clone https://github.com/senoldogann/LLM-Context-Manager.git
cd LLM-Context-Manager
cargo build --release
# Binary location: target/release/ccm-cliNo Rust toolchain? Use the Docker option in GETTING_STARTED.md.
The npm manifest is intentionally inside npm/, not the repository root:
cd npm
npm test
npm pack --dry-run
npm publish --access public --provenanceSemantic search needs no configuration: the built-in local embedding model is
the default (see Embeddings). Create ~/.ccm/.env (or start from
the repository's .env.example) only to change defaults:
# Default: built-in local model, nothing to set.
# Option B: Ollama
EMBEDDING_PROVIDER=ollama
EMBEDDING_HOST=http://127.0.0.1:11434
EMBEDDING_MODEL=mxbai-embed-large
# No API key is required for local Ollama.
# Option C: OpenAI. This one line selects it; code chunks are sent to OpenAI.
OPENAI_API_KEY=sk-your-key
# Networking & Limits
EMBEDDING_TIMEOUT_SECS=30
CCM_MAX_FILE_BYTES=2097152
# MCP Security (strict allowlist is ON by default)
CCM_ALLOWED_ROOTS=/Users/you/projects:/Users/you/sandbox
CCM_REQUIRE_ALLOWED_ROOTS=1
# MCP Runtime
CCM_MCP_ENGINE_CACHE_SIZE=8
CCM_MCP_DEBUG=0
# Re-index automatically when project files change (0 = manual index_now only)
CCM_AUTO_REFRESH=1
# Optional: disable embeddings entirely (semantic search disabled)
CCM_DISABLE_EMBEDDER=0
# Optional: embed data files (md/json/yaml) into vector search
CCM_EMBED_DATA_FILES=0
# npm wrapper security (0 = enforce checksum, 1 = bypass)
CCM_ALLOW_UNVERIFIED_BINARIES=0
# Optional download tuning (milliseconds / attempts)
CCM_DOWNLOAD_TIMEOUT_MS=120000
CCM_DOWNLOAD_ATTEMPTS=3Advanced overrides:
CCM_PROJECT_ROOTpins the default project root and overrides the workspace reported by the host. Without it the MCP server resolves its default project in this order: the workspace the host reports via MCProots→ the launch directory when it lies insideCCM_ALLOWED_ROOTS(never/or your home directory) → the singleCCM_ALLOWED_ROOTSentry.CCM_DB_PATHoverrides the default MCP vector DB location..env.examplecontains the full advanced tuning surface for chunking, batch size, hybrid ranking weights, and compatibility aliases such asOPENAI_API_KEY,CCM_SKIP_CHECKSUM,CCM_MCP_REQUIRE_ALLOWED_ROOTS,CCM_EMBED_DATA, andEMBEDDING_DISABLED.- Hybrid weight tuning details live in
docs/hybrid-ranking.md.
Default: built-in local model. With no EMBEDDING_* settings and no
OPENAI_API_KEY in ~/.ccm/.env, CCM embeds code in-process with
ibm-granite/granite-embedding-97m-multilingual-r2
(Apache-2.0; IBM's int8 ONNX export, 384-d vectors, CLS pooling, inputs truncated
to 512 tokens) through ONNX Runtime, on the physical CPU cores.
- Download: the first index (or
ccm-cli models pull) downloads ~124 MB (98 MB model + 25 MB tokenizer + configs) from Hugging Face at a pinned revision into~/.ccm/models/ibm-granite--granite-embedding-97m-multilingual-r2/<revision>/. Every file is verified against a pinned SHA-256; a mismatch or failed download is an explicit error, never a silent fallback. Only the model is downloaded; nothing about your code leaves the machine. - Offline / air-gapped: run
ccm-cli models pullon a connected machine and copy~/.ccm/modelsover; pre-placed files are used after checksum verification.CCM_MODEL_DIRmoves the models root,HF_ENDPOINTselects a mirror. - Tuning:
CCM_EMBED_THREADS(default: physical cores, capped by the available CPU quota, e.g. a container's cgroup limit). The model embeds one chunk per inference call: its int8 activations are quantized per call, so batching would make a chunk's vector depend on the chunks embedded with it.CCM_LOCAL_EMBED_BATCHsets the local inference batch (default 1);CCM_EMBED_BATCH_SIZEsets the texts per Ollama/OpenAI request (default 32). - Intel Macs (
x86_64-apple-darwin): ONNX Runtime ships no prebuilt binary for this target, so the local model is not compiled in. UntilOPENAI_API_KEYis in~/.ccm/.envorEMBEDDING_PROVIDERis set, CCM builds a graph-only index;ccm-cli doctorand the index output say why.
Optional upgrade: OpenAI. Add OPENAI_API_KEY=sk-... to ~/.ccm/.env and
CCM embeds with text-embedding-3-small on the official https://api.openai.com/v1
endpoint, which needs no CCM_ALLOW_REMOTE_EMBEDDING opt-in. Code chunks are
then sent to OpenAI. Only the key in ~/.ccm/.env counts: a key that is merely
exported in your shell never switches the provider, so code does not leave the
machine without an explicit choice. benchmarks/
compares it with the built-in model.
Provider selection: EMBEDDING_PROVIDER=local|ollama|openai wins when set.
Otherwise an OPENAI_API_KEY in ~/.ccm/.env selects OpenAI unless
EMBEDDING_HOST is set (EMBEDDING_MODEL then only picks the OpenAI model).
Without that key, setting EMBEDDING_HOST or EMBEDDING_MODEL keeps the previous
Ollama/OpenAI behavior, so existing configurations work unchanged, and with
neither the local model is used. CCM_DISABLE_EMBEDDER=1 turns semantic search off.
Changing models: the index manifest records which provider, model, revision
and dimension produced its vectors, and vectors of two models are never mixed.
After a change (including an upgrade from 0.3.x, whose Ollama-built index meets
the new local default), the MCP server re-embeds the active index once in the
background (the freshness line says semantic index being rebuilt; auto-refresh
keeps the graph fresh meanwhile and search_code uses graph results until it
finishes).
ccm-cli index / index_project re-embed on demand the same way.
If the embedding source is unavailable (Ollama or OpenAI unreachable, model
download failed),
indexing still activates a graph-only index (graph tools work, search_code
falls back to lexical matching) and reports why; the next index run fills in the
vectors. ccm-cli doctor reports the embedder state: for the local model it
checks the files without downloading and runs a probe embedding; for Ollama/OpenAI
it sends a real probe request.
Security: MCP enforces a strict allowlist by default — only directories under CCM_ALLOWED_ROOTS (falling back to CCM_PROJECT_ROOT) and workspaces the host reports via MCP roots can be indexed or read. Set CCM_REQUIRE_ALLOWED_ROOTS=0 only if you explicitly want the relaxed mode; even then, access stays confined to the startup project root.
# Index a project
ccm-cli index --path .
# Project map: files ordered by how much other files use them
ccm-cli map --path .
# Search semantically
ccm-cli query --text "authentication logic"
# Cursor prediction (file:line format)
ccm-cli query --text "src/main.rs:50"
# Watch mode - auto-reindex
ccm-cli index --path . --watch
# Diagnose installer, allowlist, index compatibility, and embedder state
ccm-cli doctor --path .
# Download and verify the built-in embedding model ahead of time
ccm-cli models pull
# Evaluate retrieval quality
ccm-cli eval --tasks eval/golden_tasks.v3.ccm.jsonBy default, MCP discovery exposes only context and index_project; this is
intentional so unused schemas do not consume every agent session's input budget.
The specialized tools below remain directly callable for compatibility. Set
CCM_MCP_LEGACY_TOOLS=1 only when a client needs the full legacy surface in
tools/list.
| Tool | Answers | Example |
|---|---|---|
context |
Default one-call task context; CCM chooses exact graph lookup or hybrid retrieval and enriches the best match | context {query:"change auth timeout safely"} |
map |
What is in this project and what matters most | map {path:"src"}: files by cross-file use, with their top symbols |
explain |
Everything about one symbol in one call | explain {target:"Engine.start"}: definition, body, members, callers, callees, tests |
find_usages |
Who uses a symbol, and how | "Who calls this function?" |
impact_of_change |
What may break if a file changes | Dependents across the codebase |
search_code |
Code by meaning or name | "Find auth handling" |
find_nodes |
Symbols by name or path | find_nodes {query:"UserService"} |
trace_call_chain |
How one symbol reaches another | from → to path |
diff_context |
Recently changed code via git | Last N days of changes |
index_project |
Refresh the project index | Incremental; mode:"quick" skips embeddings and upgrades them in the background |
index_now |
Index and wait for the final stats | mode:"quick", "full" or "upgrade" |
Use context first for normal agent work. Its packet may include the selected
definition/body plus members, callers, callees, tests and short related matches
inside one max_tokens budget. The specialized list tools keep the compact
- Kind: name · path:start-end · relation format. target accepts a name
(run, Engine.start), file path, path:line or node ID; ambiguous exact
targets are reported rather than silently chosen.
0.4.0 (breaking):
get_contextandread_graphwere replaced byexplain. Graph tools return compact one-line results without node IDs and take amax_tokensbudget instead ofmax_chars(still accepted as characters / 4); passpath:lineastarget.
The first index builds the complete index. Later index_project or index --watch runs compare the filesystem manifest and update only changed or newly created files while removing deleted-file nodes. The vector database is not rebuilt when nothing changed. Cross-file reference edges are refreshed from the in-memory semantic graph so callers remain accurate after a symbol changes. Retrieval tools fail fast when an index is missing or being updated; call index_project explicitly instead of making a search request wait for a hidden rebuild.
For large repositories, MCP index_project returns before the client's timeout
and continues in the background. Call the tool again to poll until it returns
the final indexing statistics.
For fast first-response workflows, call index_project or index_now with
mode: "quick". This scans and parses sources, builds the navigation graph, and
returns nearly immediately without waiting on the embedding provider. A
background task then fills the semantic vectors from the same graph; until that
completes, search_code serves graph-ranked results instead of failing. Use
mode: "upgrade" (or index_now with "upgrade") to repair or complete a
graph-only index that was created while the embedding backend was unavailable,
without re-parsing source files.
Full rebuilds are prepared in a staging generation. A scan, parse, embedding or vector write failure leaves the previous graph, manifest and vector table in place. File fingerprints include content, so same-size edits with preserved timestamps are still detected.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ AI Agent │────▶│ MCP Server │────▶│ Core Engine │
│ (Claude) │◀────│ (ccm-mcp) │◀────│ (Rust) │
└─────────────┘ └─────────────┘ └─────────────┘
│
┌────────────────────────────┼────────────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Code Graph │ │ Vector DB │ │ Parser │
│ (Petgraph) │ │ (LanceDB) │ │(Tree-sitter)│
└─────────────┘ └─────────────┘ └─────────────┘
| Language | Extensions | Analysis |
|---|---|---|
| Rust | .rs |
Full AST |
| Python | .py |
Full AST |
| TypeScript | .ts, .tsx |
Full AST |
| JavaScript | .js, .jsx |
Full AST |
| Go | .go |
Full AST |
| Java | .java |
Full AST |
| Kotlin | .kt, .kts |
Full AST |
| C# | .cs |
Full AST |
| C | .c, .h |
Full AST |
| C++ | .cc, .cpp, .cxx, .hh, .hpp, .hxx |
Full AST |
| Ruby | .rb, .rake, .gemspec |
Full AST |
| PHP | .php, .phtml |
Full AST |
| Swift | .swift |
Full AST |
| Config/Data | .md, .json, .yaml |
Full File |
CCM includes a golden task evaluation framework:
# Run evaluation
ccm-cli eval --tasks eval/golden_tasks.v3.ccm.json
# Compare structural vs hybrid scoring
ccm-cli eval --tasks eval/golden_tasks.v3.ccm.json --compareIf the evaluation index is missing, CCM bootstraps it automatically before scoring.
Semantic search_code tasks still require a configured embedder.
Regression gate (not a quality measure): CI runs a synthetic task set,
eval/fixtures/golden_tasks.synthetic.json,
with deterministic fixture embeddings in eval.yml
and fails on any drop (--min-pass-rate 100). It guards against regressions;
it says nothing about retrieval quality on real code. The external benchmark
below is a 35-task pilot; its get_context/read_graph tasks are cursor and
neighbourhood lookups, not agent tasks.
External benchmark (v0.3.13): 35 hand-verified golden tasks on real repos
(serde, flask, express) with real Ollama embeddings. Hybrid scoring passes
82.9% (29/35) vs 80.0% (28/35) semantic-only; get_context/read_graph are
20/20. On search_code alone Recall@5 is 0.600 vs 0.533. The built-in local
model (the default) reaches Recall@5 0.600 semantic-only and 0.667 hybrid (MRR
0.419 and 0.497 vs 0.352 and 0.436 for mxbai) and indexes 3–4× faster on CPU
(19–23 ms vs 68–81 ms per chunk on an Apple M4). Full numbers, failure ledger and reproduction steps:
benchmarks/README.md.
CCM release builds are designed to be reproducible and install-safe:
- GitHub Releases publish platform binaries and
checksums.txt - The npm wrapper verifies downloaded binaries before first use
- MCP transport now enforces request size limits and redacts sensitive debug payloads
- The release workflow builds Linux, macOS, and Windows artifacts before attaching release assets
- npm publication is a separate manual step from
npm/after the GitHub Release assets are attached - The README quick-start now matches the same smoke path we use for first-install checks
For local source builds, cargo build --release still requires protoc to be installed on your machine.
- Run
ccm-cli index --path .first - If you override it, check
CCM_PROJECT_ROOTmatches the indexed directory - Run
ccm-cli doctorto check the embedder (local model files, or your Ollama/OpenAI service)
- The first run downloads the local embedding model (~124 MB, once);
ccm-cli models pullfetches it ahead of time - Subsequent runs are fast (incremental)
- Ensure the GitHub Release includes
checksums.txt - Re-run the install once
- As a last resort, set
CCM_ALLOW_UNVERIFIED_BINARIES=1to bypass verification
- Strict allowlist mode is enabled by default
- Set
CCM_ALLOWED_ROOTSto include the project root - Only if you really need it, disable strict mode with
CCM_REQUIRE_ALLOWED_ROOTS=0(access still stays within the startup project root)
- Increase
CCM_MAX_FILE_BYTESif you need larger text files indexed
- By default, data files (
.md,.json,.yaml) are indexed but not embedded. - Enable
CCM_EMBED_DATA_FILES=1to include them in semantic search.
- NPM Package: @senoldogann/context-manager
- Turkish README: README.tr.md
- Getting Started: GETTING_STARTED.md
- Environment Example: .env.example
- Hybrid Ranking Notes: docs/hybrid-ranking.md
- Contributing: CONTRIBUTING.md
MIT License - Open source and free to use.
