Local code graph for AI coding agents. Indexes repository into embedded graph DB (DuckDB/SQLite), exposes 50+ MCP tools to Claude Code, Cursor, Codex, and Gemini. Federates across sibling repos.
Strong foundation with 16 well-intentioned tools covering code navigation, indexing, and analysis. Most tools have substantive descriptions (averaging 180-320 chars, well within 10-1024 range) and input schemas with type definitions. However, several critical gaps prevent higher scores: (1) Output schemas are described in prose within tool descriptions rather than formally documented as JSON Schema objects that LLMs can parse programmatically. (2) Tool naming is inconsistent, some use clear action verbs (search_docs, fetch_and_index) while others use nouns (architecture_overview, domain_map, hotspots) that don't signal intent until the description is read. (3) Several tools lack error handling guidance and recovery paths. (4) Parameter descriptions occasionally reference complex domain logic without constraining values (e.g., fetch_and_index accepts any URL without clear SSRF policy details in the param description itself).
Add an external directory to the code graph and hot-index it. Use this when the user wants to include a related repo or sub-project in the graph so cross-repo symbol lookups work (e.g., a frontend repo while this MCP server runs inside the backend repo).
Compact map of the codebase grouped by architectural layer + role. CALL THIS FIRST when the user asks a broad question like "how does X work", "where should I add Y", "explain the structure". Cheaper than reading files: returns at most ~200 lines of JSON. Output shape: { "presentation": { "router": [{path, module_doc, symbols}, ...], "component": [...], ... }, "application": { "handler": [...], ... }, "domain": { "model": [...], "schema": [...] }, "infra": { "provider": [...], ... }, "test": { "test": [...] }, "doc": { "doc": [...] } }
Return the heading outline (table of contents) of a Markdown file. Shows the hierarchical structure of sections with line numbers.
Find all Markdown documentation that references a code symbol. Use this to find docs about a function or class before reading its code.
All files related to a domain/feature keyword, grouped by role. Use when the user names a concept ("stats", "donor merge", "Cerfa") to find every handler/router/model/etc touching it, faster than grep because it respects the role taxonomy. Matches against file path, role, and module_doc (case-insensitive).
Output schemas not formally documented as JSON Schema objects. Tool descriptions include prose-format output examples (e.g., 'Returns: [{method, path, framework, ...}]' or 'Output shape: {...}') but LLMs cannot parse these programmatically. This violates pattern:tool-description requirement for structured, machine-parseable schema documentation.
Inconsistent tool naming convention. 16 tools mix noun-based names (architecture_overview, domain_map, hotspots, findings) with verb-noun pairs (search_docs, fetch_and_index). Noun-based tools require reading the description to infer action intent, increasing LLM selection latency and error likelihood. Baselines show 90% of A+ tools start with action verb (get_, list_, create_, search_, update_, delete_, send_).
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 64 | 2026-07-28+ | v2 |
List HTTP endpoints in the codebase. Optional filters: path_pattern, glob like "*/donations*" or "/api/stats/*" method, GET / POST / PUT / PATCH / DELETE (case-insensitive) Returns: [{method, path, framework, file, line, handler}] grouped by framework. Includes both FastAPI decorators and Nuxt server/api routes, so cross-repo questions ("does the frontend call this Python route?") become navigable.
Fetch a URL, reduce it to text, chunk and index it for search_fetched. http/https only; private, loopback and link-local hosts are refused (SSRF); refused in secure mode unless [codegraph] allow_fetch is set; every fetch is audited. A re-fetch inside ttl_hours returns the cached count.
Query scanner findings: what cgh knows about files beyond their code structure. Keys are namespaced by the scanner that wrote them (pii.email, secret.aws_key, confidential, summary, ...). Args: file_path: restrict to one file (absolute, or relative to the repo root). Empty = all files. key_prefix: e.g. "pii." or "secret". Empty = every key. severity: "info" | "warn" | "block". Empty = all severities. limit: per scope (default 100). Federated: children's finding stores are read read-only and every row carries a `scope` field (parent / <subrepo-name>).
Force-index specific files or directories, even if they are in .gitignore or .git/info/exclude. Bypasses all ignore rules and mtime cache. IMPORTANT: This bypasses safety filters. Always confirm with the user first. Call once with confirmed=False (default) to preview what will be indexed, then call again with confirmed=True after user approval. Args: paths: list of file or directory paths (relative to repo root or absolute) confirmed: must be True to actually index. False returns a preview only.
Change-risk hotspots: files that churn a lot AND are central to the import graph. High-churn code that many files depend on is where a regression hurts most, so this surfaces refactor / review targets. We join two signals per file: - churn: commit count + recency, from `git log` over the parent repo (analysis.churn.file_churn, bounded to the last N commits). - centrality: in-degree, the number of files that import this one, counted over the IMPORTS edge via the GraphDB protocol. Score formula (each term in 0..1, higher is riskier): commit_term = log1p(commits) / log1p(max_commits) import_term = log1p(importers) / log1p(max_importers) recency_term = 1 / (1 + age_days / 30) # ~1 today, ~0.5 at 30d score = round(100 * (0.45*commit_term + 0.35*import_term + 0.20*recency_term), 2) Churn dominates, centrality is the multiplier that says "and it matters", recency is a lighter freshness nudge. log1p compresses a few hot files so they do not crush the scale. Args: limit: how many top files to return (default 20). Returns JSON {hotspots: [{file, commits, last_modified, importers, authors, score}], count, scanned, note}. NOT federated: git churn is the parent repo's history only.
Surgical reindex: compare stored git blob SHAs to the current HEAD and re-index only files whose content changed. Much faster than scan_repo after `git pull`, `git checkout <branch>`, or `git rebase`. Falls back automatically to a full scan if the index is too old (pre-0.4 DB without blob SHA tracking). Returns JSON: {mode, reindexed_count, deleted_count, unchanged_count, errors, elapsed_s}.
Full re-index of the entire repository. Call this after major changes (branch switch, rebase, pull) to refresh the graph. Returns stats: files indexed, errors, time elapsed.
Report whether the code graph is fresh relative to the current git HEAD. Call this BEFORE trusting symbol_lookup/find_callers results if the user mentions a branch switch, rebase, pull, or recent edits. When `fresh` is false, call scan_repo to refresh the index. Returns JSON with: fresh, true if indexed sha == HEAD and working tree is clean indexed_sha, git commit the graph was built at indexed_at, ISO timestamp of last scan current_sha, git HEAD now behind_by, commits between indexed and HEAD dirty, working tree has uncommitted changes changed_files, files modified since indexed_sha (up to 200)
Search documentation (Markdown files) by heading title or body content. Returns matching sections with file path, line range, and body preview. Use this to find relevant documentation before diving into code. Federated across subrepos.
Search content previously pulled in by fetch_and_index. No network: reads the local index. Returns url, title, snippet.
Who knows this file: the top authors by commit count and recency, rolled up from `git log -- <file>`. Use it to find a reviewer or to learn who last touched code you are about to change. Args: file_path: repo-relative or absolute path to the file. Returns JSON {file, authors: [{name, commits, last_commit}], note}. last_commit is a unix timestamp (seconds). NOT federated: ownership is computed from the parent repo's git history.
fetch_and_index violates pattern:tool principle 'one tool = one job'. Tool name contains 'and', signaling dual responsibility (fetch URL + index content). This should be split into 'fetch_url' and 'index_content' or renamed 'index_url'. Current naming invites LLM confusion about whether both steps always execute.
Error handling and recovery guidance missing from most tools. Examples: search_docs returns 'matching sections' but does not specify behavior when query matches zero results or exceeds limit. fetch_and_index mentions SSRF policy in description but does not document what error the LLM receives if a private host is passed (e.g., 'Refused: host is in private range. Use allow_fetch config.'). Pattern:recovery-guide requires error responses that tell the LLM what to do next.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). Six tools have Risk field documented (WRITE or READ_ONLY) in source code comments, but this metadata is not declared in the MCP tool definition where the protocol can parse and enforce it. Pattern:tool-annotation enables protocol-level access control and auditing.
Parameter constraints under-specified. Example: fetch_and_index accepts 'url' as a string with minimal validation hint ('http/https only, no private hosts') in description, but the param itself does not declare a regex pattern, enum, or length limit. Pattern:constrained-input requires formal constraints be machine-parseable.