MCP Tools

rag-rat mcp starts a local rmcp STDIO server. The server is read-only for configured source roots; it may update the configured SQLite index when automatic stale-index healing is needed. It opens the index through the configured root, so search and graph tools resolve the active git commit plus dirty worktree overlay in the same way as CLI queries.

Install

The MCP server is launched by the MCP client over STDIO; it does not listen on a TCP port. The recommended setup is rag-rat init from the target repo, which indexes it and prints the one command that registers a project-scoped MCP server (init does not register it for you). The per-repo setup, by hand:

cd /path/to/your/repo
cargo install rag-rat                 # or: cargo install --path crates/rag-rat-cli --bin rag-rat
rag-rat index --discover
rag-rat models install sentence-transformers/all-MiniLM-L6-v2
rag-rat reconcile --changed-first --limit 500 --batch-size 64
rag-rat doctor

Use embedding-hash (built with --no-default-features) instead of FastEmbed when a small dependency footprint matters more than real semantic embeddings:

rag-rat models install embedding-hash

Register a project-scoped server that runs in the repo so it resolves that repo’s rag-rat.toml:

claude mcp add --scope project rag-rat -- rag-rat mcp

or a project .mcp.json / equivalent:

{
  "mcpServers": {
    "rag-rat": { "command": "rag-rat", "args": ["mcp"] }
  }
}

Do not register a single global/user-scoped server with a hardcoded --config /some/repo/...: it would serve that one repo’s index and memories in every project. Keep the server project-scoped and let it find rag-rat.toml from the repo it runs in. --config <path> remains available for a specific profile, and rag-rat mcp --json switches tool results from TOON to JSON.

To run from a checkout without installing, use a project-scoped server whose command is cargo run --manifest-path /path/to/rag-rat/Cargo.toml --bin rag-rat -- mcp.

Tools

The MCP surface is 47 tools in nine groups. The signatures are below; the prose sections that follow describe response shapes and per-tool behavior. Every symbol/search/read tool additionally accepts an optional "worktree": string — an absolute path to a linked git worktree, served as a branch overlay over the indexed checkout — omitted from the signatures below for brevity.

Search & symbols

  • semantic_search: { "query": string, "limit"?: number, "include_graph"?: "none" | "compact" | "full", "graph_limit"?: number, "include"?: ("generated" | "git" | "papertrail" | "fallback")[], "explain"?: boolean }
  • symbol_lookup: { "symbol"?: string, "ref"?: string, "id"?: string, "lang"?: string, "allow_ambiguous"?: boolean, "limit"?: number, "include"?: ("memories" | "generated")[] }
  • docs_for_symbol: { "symbol"?: string, "ref"?: string, "id"?: string, "allow_ambiguous"?: boolean, "limit"?: number }

Call graph & impact

  • find_callers: { "symbol"?: string, "ref"?: string, "id"?: string, "resolution"?: "exact" | "syntactic" | "fuzzy", "allow_ambiguous"?: boolean, "limit"?: number, "include"?: ("references" | "unresolved" | "macros" | "common_methods" | "coverage" | "memories")[], "edge_kinds"?: string[] }
  • trace_callees: { "symbol"?: string, "ref"?: string, "id"?: string, "resolution"?: "exact" | "syntactic" | "fuzzy", "allow_ambiguous"?: boolean, "limit"?: number, "include"?: ("references" | "unresolved" | "macros" | "common_methods" | "coverage" | "memories")[], "edge_kinds"?: string[] }
  • impact_surface: { "query"?: string, "symbol"?: string, "ref"?: string, "id"?: string, "resolution"?: "exact" | "syntactic" | "fuzzy", "allow_ambiguous"?: boolean, "limit"?: number, "include"?: ("tests" | "docs" | "git" | "papertrail" | "text_fallback" | "memories")[], "full_memories"?: boolean }
  • check_library_usage: { "path"?: string, "package"?: string, "deprecated_only"?: boolean, "limit"?: number }
  • compare_graph_to_text: { "symbol"?: string, "ref"?: string, "id"?: string, "pattern": string, "resolution"?: "exact" | "syntactic" | "fuzzy", "allow_ambiguous"?: boolean, "limit"?: number, "include"?: ("tests" | "references" | "unresolved" | "macros" | "common_methods")[], "edge_kinds"?: string[] }
  • compare_graph_to_scip: {}
  • ffi_surface: { "limit"?: number }

Orientation & ranking

  • repo_brief: { "mode"?: "spine" | "churn" | "god_modules" | "refactor_candidates", "limit"?: number, "include"?: ("memories" | "generated")[] }
  • repo_clusters: { "limit"?: number, "include"?: ("memories" | "generated")[], "min_cluster_size"?: number }
  • important_symbols: { "limit"?: number, "personalize"?: string[] }

Clones

  • find_clones: { "min_similarity"?: number, "min_copies"?: number, "limit"?: number }
  • clones_for_symbol: { "id"?: string, "ref"?: string, "path"?: string, "line"?: number }

Read

  • read_chunk: { "chunk_id": number, "include_graph"?: "none" | "compact" | "full", "graph_limit"?: number, "include"?: ("memories")[] }

Git & tracker history

  • commit_search: { "query": string, "limit"?: number }
  • git_history_for_path: { "path": string, "limit"?: number }
  • git_history_for_symbol: { "symbol"?: string, "ref"?: string, "id"?: string, "lang"?: string, "allow_ambiguous"?: boolean, "limit"?: number }
  • commits_touching_query: { "query": string, "limit"?: number }
  • git_blame_chunk: { "chunk_id": number }
  • papertrail_for_chunk: { "chunk_id": number, "limit"?: number }
  • papertrail_for_symbol: { "symbol"?: string, "ref"?: string, "id"?: string, "lang"?: string, "allow_ambiguous"?: boolean, "limit"?: number }
  • papertrail_for_commit: { "commit_hash": string, "limit"?: number, "include"?: ("fallback")[] }
  • papertrail_issue_search: { "query": string, "limit"?: number }
  • papertrail_refs_for_path: { "path": string, "limit"?: number }
  • rationale_search: { "query": string, "limit"?: number, "include"?: ("fallback")[] }

Index health

  • llm_status: {}
  • heal_index: { "limit"?: number }
  • papertrail_sync_status: {}
  • index_status: {}

Repo memories

  • memory_create: { "kind": MemoryKind, "title": string, "body": string, "confidence": "high" | "medium" | "low", "created_by"?: string, "source"?: "agent" | "human" | "imported" | "generated", "tags"?: string[], "payload"?: object, "bind"?: BindTarget }
  • memory_rebind: { "memory_id": string, "bind": BindTarget }
  • memory_update: { "memory_id": string, "kind"?: MemoryKind, "title"?: string, "body"?: string, "confidence"?: "high" | "medium" | "low", "status"?: "active" | "stale" | "obsolete" | "rejected", "tags"?: string[], "payload"?: object }
  • memory_search: { "query": string, "limit"?: number }
  • memory_for_symbol: { "symbol"?: string, "ref"?: string, "id"?: string, "allow_ambiguous"?: boolean, "limit"?: number }
  • memory_for_path: { "path": string, "limit"?: number }
  • memory_for_call_path: { "edge_sequence_hash": string, "limit"?: number }
  • memory_show: { "memory_id": string }
  • memory_validate: {}
  • memory_doctor: {}
  • memory_mark_obsolete: { "memory_id": string }
  • memory_edge_add: { "source_node_id": string, "relation": "depends_on" | "relates_to" | "supersedes" | "derived_from" | "tracks", "target_node_id"?: string, "target_repo_id"?: string, "github_owner"?: string, "github_repo"?: string, "github_number"?: number }
  • memory_edge_remove: { "edge_key": string }
  • memory_edges: { "direction": "from" | "into", "node_id"?: string, "github_owner"?: string, "github_repo"?: string, "github_number"?: number }

MemoryKind is one of Invariant, Decision, RejectedAlternative, Risk, BugPattern, TestExpectation, PerformanceNote, SecurityNote, FFIBoundary, PlatformQuirk, FollowUp, OpenQuestion, Task, Concept, Obsolete. BindTarget names exactly one anchor — see Repo memories below for its shape. payload is accepted only for the polymorphic graph-node kinds Task and Concept (#465); passing it for any other kind is rejected.

Dream (memory maintenance)

  • dream: { "limit"?: number, "all"?: boolean }
  • dream_review: { "finding": string, "verdict": "accept" | "dismiss" | "reset" }

The include array replaces the former include_* boolean params (#149 follow-up — shorter, token-cheaper). Presence in the list turns a section on. Omit include entirely to keep each tool’s defaults (e.g. impact_surface returns tests/docs/git/papertrail/text_fallback/memories; graph tools return memories). A present list is the EXACT on-set — so it’s also how you disable a default-on section: impact_surface with "include": ["git"] returns git history only, and any tool with "include": [] returns the bare result. Each tool accepts only its own flags (see the signatures above). The server also accepts a JSON-string-encoded array ("[\"git\"]") as well as a real array, so the surface works even from clients that serialize array params as strings (anthropics/claude-code#24599).

Compact vs full memories. impact_surface’s repo_memories payload is compact by default (#37): each memory is summarized to a scannable header — memory_id, kind, title, confidence, status, and its primary binding’s anchor_status / binding_kind / path / span / id — so an agent skims the high-signal facts without scrolling full bodies. The lane shape is unchanged (direct / path_crossed / call_path_crossed / stale). Pass "full_memories": true for the full bodies + every binding + call paths, or fetch full detail per-anchor via memory_for_symbol / memory_for_path / memory_for_call_path. (full_memories is orthogonal to include: drop "memories" from include to omit the lane entirely.)

tools/list is served by rmcp and exposes typed JSON schemas derived from the same request structs used by the handlers. Existing tool names and response fields are kept stable for current MCP clients.

Response encoding (TOON)

Tool results are returned as TOON (Token-Oriented Object Notation), not JSON. TOON is a token-efficient text encoding that renders uniform result rows (caller lists, symbol candidates, clusters) as a dense [N]{cols}: table — measured ~30% smaller than compact JSON on those payloads, and never larger in practice (it ties compact JSON on nested shapes). Because MCP results are text content consumed by an LLM, the denser encoding is both valid and cheaper. There is no per-call flag; MCP output is always TOON. The JSON snippets below describe the logical shape of each response — the same fields, just shown in JSON for readability. (The rag-rat CLI mirrors this: it defaults to TOON and takes a global --json flag for JSON output.)

symbol_lookup is the candidate-selection step for symbol-shaped tools. It returns:

{
  "candidates": [
    {
      "id": "sym_a3f29c1b",
      "logical_variant_count": 2,
      "logical_group_reason": "cfg_variant",
      "ref": "crates/app/src/runtime/task_spawn.rs::spawn_blocking",
      "qualified_name": "crates/app/src/runtime/task_spawn.rs::spawn_blocking",
      "kind": "function",
      "signature": "pub(crate) async fn spawn_blocking<F, T>(...)"
    }
  ],
  "disambiguation_required": true
}

Graph, docs, git-history, papertrail, and symbol-specific impact tools accept id, ref, or symbol and resolve them in that priority order. id is the opaque, stable handle a symbol_lookup (or any symbol-returning tool) emits — a sym_<hex> token; copy it verbatim and pass it back, never parse it as a number. There is no numeric symbol_id on the wire: it’s an internal rowid reassigned on every reindex, so it could not be cached safely across an edit (#149). If a bare symbol maps to multiple candidates, the tool returns the same candidate object instead of guessing. Pass allow_ambiguous: true only for navigation/debugging when name fallback is acceptable.

Logical symbols group multiple concrete symbol rows that represent one source-level API, such as Rust #[cfg] variants of the same function in the same file. Use id with resolution: "exact" when callers target the logical API rather than one cfg body. Exact logical mode only returns edges whose verified target_symbol_id is a member of the group. Graph envelopes include the selected logical_symbol and its variants; cfg_expr may be null until cfg attribute extraction is recorded by the parser.

Search tools return chunk IDs, paths, line spans, current-text snippets in the summary field, and scores. Search and read_chunk validate stored chunk anchors against current source before returning context; stale files are reindexed once per tool call and their SQLite FTS5 rows are updated before retrying. Use read_chunk only after a search or lookup has narrowed the context.

Auto-heal is capped at four files per call. If more stale files are detected, tools return a needs_reindex error instead of doing unbounded work. Deleted source for a requested chunk returns Gone; a chunk that disappears after file reindex returns StaleChunk.

index_status reports content_revision, fts_source_revision, fts_dirty, and fts_fresh. Search tools synchronize dirty or stale SQLite FTS state before querying so results are not served from an outdated FTS table.

semantic_search graph and evidence controls:

  • include_graph: none, compact, or full. Default is compact.
  • graph_limit: maximum caller/callee/import/type evidence entries to attach. Default is 3 for search and 20 for read_chunk.
  • include: array of generated (index generated files), git (git-history ranking boosts), papertrail (cached tracker papertrail boosts), fallback. git and papertrail are on by default — omit include to keep them; pass an explicit list to override.
  • explain: include score components (bm25, vector, symbol, graph, git, papertrail). Default is false.

Every hit also carries retrieval_mode (lexical, vector, or hybrid) — always present, so a caller knows whether embeddings contributed without setting explain. It is lexical for every hit when no embedding model is active.

Graph tools are backed by tree-sitter-derived syntax edges. Edge kinds are imports, exports, calls_name, constructs, dispatches, uses_macro, uses_operator, uses_precedence_group, references_type, implements, and contains; confidence is reported as edge_confidence (confidence is retained as the compatibility alias) with values Exact, Syntactic, NameOnly, or Ambiguous. Graph evidence is syntactic, confidence-labeled evidence, not compiler-grade name resolution or hard truth. Search results default to compact graph evidence with bounded caller/callee lists; read_chunk defaults to full graph evidence. Caller and callee entries include exact tree-sitter callsite spans: callsite.path, callsite.line, and callsite.span ([start_line, end_line]).

Graph tools and impact_surface accept resolution:

  • exact: only verified target-symbol rows are returned. A row is allowed only when the edge’s verified target resolves to the selected symbol (its id/ref), or the resolved fully-qualified symbol identity matches symbol. Every returned row has verified_target_symbol: true.
  • syntactic: default. Exact matches plus qualified syntactic evidence are returned. Unresolved qualified call targets may be shown, but broad bare-name ambiguous fallback is excluded.
  • fuzzy: compatibility/navigation mode. Suffix and bare-name fallback are allowed, including ambiguous candidates. Treat these rows as possible evidence, not proof.

Use id (the sym_<hex> handle from a previous symbol_lookup) with resolution: "exact" to target a resolved symbol — including the cfg/duplicate-variant case, where the handle addresses the whole logical group (all its members). Bare names in exact mode intentionally return little or nothing unless id is provided.

find_callers and trace_callees return a graph envelope, not a bare row array:

{
  "query": {
    "tool": "find_callers",
    "id": "sym_a3f29c1b",
    "ref": "crates/app/src/runtime/task_spawn.rs::spawn_blocking",
    "resolution": "exact"
  },
  "summary": {
    "returned_count": 38,
    "total_matching_edges": 38,
    "truncated": false,
    "exact_verified": 38,
    "syntactic": 0,
    "name_only": 0,
    "ambiguous": 0,
    "unresolved": 0,
    "false_positive_risk": "low",
    "completeness_risk": "low"
  },
  "coverage": {
    "indexed_files": 1055,
    "parser_failures": 0,
    "stale_files": 0,
    "known_index_gaps": [],
    "parser_coverage_for_paths": []
  },
  "results": []
}

The trust signal is in summary and coverage: exact mode should have exact_verified == total_matching_edges, truncated: false, no parser failures for covered paths, stale_files: 0, and completeness_risk: low. If the source changed after indexing, stale_files is non-zero for the covered paths and the result should be treated as needing reindex before audit use. When strict graph rows are verified but same-name unresolved callsites exist, known_index_gaps contains a plain-language note such as 31 unresolved qualified callsites match the requested final segment but are not verified to this symbol.

trace_callees is repo-relevant by default. It returns verified or syntactic calls_name edges and verified/repo-local constructs edges. It hides unresolved calls, unresolved macros, type references, imports/exports, common method/combinator calls, and std/common constructors unless a caller asks for them explicitly:

Pass these in the include array (each off by default; memories is on by default):

  • unresolved: include unresolved qualified/name-only calls.
  • macros: include uses_macro edges such as format!, json!, and vec!.
  • common_methods: include common low-signal calls such as clone, map, map_err, and_then, unwrap_or, unwrap_or_else, to_string, to_owned, as_ref, as_mut, get, insert, and unresolved/common new.
  • references: include type references, imports, exports, contains, and implements.
  • coverage (graph tools): attach the parser/coverage block. memories: attach crossing repo memories (on by default; pass "include": [] to drop them).

find_callers uses exact target resolution first, then qualified-name and target-name fallbacks; fallback hits are labeled with resolution, verified_target_symbol, raw evidence, and optional receiver_hint so clients can treat name-only or ambiguous edges as possible evidence instead of compiler truth. Rust macro invocations are stored as uses_macro and are not resolved to same-named normal modules or functions.

compare_graph_to_text is the graph-vs-rg audit bridge. It resolves a selected symbol, runs the same reverse caller graph traversal used by find_callers, runs a line-oriented regex search over currently indexed source files, then compares (path, line) sets:

{
  "query": {
    "id": "sym_a3f29c1b",
    "ref": "crates/app/src/runtime/task_spawn.rs::spawn_blocking",
    "pattern": "crate::runtime::task_spawn::spawn_blocking\\(",
    "resolution": "syntactic"
  },
  "summary": {
    "text_hits": 38,
    "graph_hits": 31,
    "matched": 29,
    "graph_only": 2,
    "text_only": 9,
    "likely_parser_gaps": 7,
    "likely_false_positives": 1,
    "complete": false,
    "recommended_fallback": "both"
  },
  "matched_hits": [],
  "text_only_hits": [],
  "graph_only_edges": [],
  "likely_parser_gaps": [],
  "likely_false_positives": []
}

Matching is by path + callsite line. Text hits with no graph edge at that location are reported as text_only_hits and copied into likely_parser_gaps; graph edges with no text match are reported as graph_only_edges. Exact or logical graph-only rows may be legitimate imported, aliased, or unqualified calls whose source line does not match the supplied regex. Fuzzy/name-only graph-only rows are also listed in likely_false_positives. The tool includes the same coverage object as graph traversal envelopes so audit output can be checked for stale source and parser failures before treating the counts as authoritative.

compare_graph_to_scip is the graph-vs-compiler audit bridge — where compare_graph_to_text diffs the tree-sitter graph against a regex, this diffs it against the SCIP oracle. It reports only contradictions: edges where the heuristic resolved a callee to one target and the compiler says it is actually another. Calls the heuristic merely missed or under-resolved are agreement-shaped (the oracle classifies them Upgrade / ResolvedExternal) and are not reported — so an empty result means “no contradictions,” not “SCIP found no missed edges.” It takes no arguments ({}) and reports over the whole oracle-covered checkout; it requires rag-rat oracle run to have populated compiler verdicts first, and returns nothing when no oracle data exists. Use it to debug the resolver where tree-sitter and the compiler actively disagree. See oracle.md.

impact_surface is the graph-backed rg replacement path for a selected symbol. For id, ref, or symbol requests it returns sections instead of a flat list:

  1. direct_semantic_callers
  2. direct_semantic_callees
  3. import_export_dependents
  4. tests_touching_symbol_path
  5. docs_mentioning_symbol_path
  6. text_fallback_hits
  7. recent_commits_touching_symbol_path
  8. papertrail_rationale_items
  9. completeness_and_caveats

Use the same disambiguation controls as graph tools. resolution: "exact" means direct graph callers/callees are verified against the selected symbol; syntactic allows qualified tree-sitter evidence; fuzzy is for navigation only. The sections are controlled by the include array — tests, docs, git, papertrail, text_fallback, memories, all on by default (omit include to keep them; pass e.g. ["git"] to narrow to git history only). If exact graph callers are empty but text fallback finds symbol/path hits, the caveats section explicitly says that graph extraction or resolution gaps are likely. Free-text query requests retain the older flat impact item shape for compatibility.

check_library_usage reports how the code uses its EXTERNAL libraries, from the SCIP oracle’s external symbol info (index.external_symbols, parsed into the external_symbols table by oracle run — data the reader used to discard). It joins every resolved-external call site to its dependency contract by moniker and groups per dependency symbol. Each entries[] element carries the moniker, package, kind, display_name, the dependency’s current signature_text / signature_language / documentation (context — reason about arity / misuse yourself, nothing is asserted), the deprecated flag (the one asserted verdict — a deterministic “deprecated” marker in the docs/signature), and the call_sites (path + line span) with a call_count. The report also returns total_external_call_sites, distinct_monikers, deprecated_call_sites, and call_sites_without_signature_info (external calls whose moniker had no contract — coverage transparency, not a finding). Filter with path (a file or directory prefix), package (a single dependency), and deprecated_only. status is ok, no_oracle_run (run rag-rat oracle run first), or no_external_symbols (the indexer emitted no external symbol info). It does NOT assert arity or removed/renamed drift: call-site arg counts are not instrumented and a “removed” verdict needs a cross-version baseline (re-index on lockfile change) — both are follow-ups. See oracle.md.

Git history tools return historical evidence. commit_search searches commit subjects and bodies; git_history_for_path returns commits touching a current path; git_history_for_symbol resolves the current symbol path/range first, then returns path history; commits_touching_query combines commit-message evidence with current file-change evidence; git_blame_chunk computes blame lazily and caches it by source_text_hash.

index_status.git_history reports whether git history is available, current git HEAD, indexed HEAD, commit count, and file-change count. Non-git roots report unavailable history without failing source or docs indexing.

Papertrail tools read the local cache only. rag-rat papertrail sync mirrors every resolved tracker binding through its native provider client; --full forces a complete historical re-walk to heal cached rows. Source and commit references remain annotations and do not select what the mirror fetches.

Papertrail outputs keep current_source separate from evidence. Cached snippets are labeled as historical tracker evidence (historical_tracker / literal_tracker_ref) and classified as decision, rejected_alternative, constraint, risk, obsolete, or context. Each evidence row names its tracker (github), project (owner/repo), item_kind (issue | change_request), item_key, and doc_kind (item | comment).

index_status.papertrail reports cached refs, issues, change requests, comments, last sync time, and each binding’s authentication and native synchronization capability. papertrail_sync_status returns that cache section directly. heal_index repairs or removes already-indexed files whose current source no longer matches the stored SQLite index, then refreshes SQLite FTS. It also probes every FTS mirror with a ranked query — the only read that decodes the docsize shadow, whose corruption every ordinary integrity check misses — and rebuilds corrupt mirrors from their durable sources, reporting them in fts_healed (or fts_deferred when a staged rebuild in flight makes the repair unsafe — rerun once it completes). It does not discover brand-new files; run rag-rat index for discovery.

Embedding artifacts are explicit and current-only. llm_status reports embedding model state, artifact counts, FastEmbed build/cache/model details, and the last reconcile throughput summary when available. The CLI-only models install embedding-hash command selects the deterministic baseline embedder. Building with --features fastembed enables the real local sentence-transformers/all-MiniLM-L6-v2 backend; models install sentence-transformers/all-MiniLM-L6-v2 is the intended FastEmbed cache-population step. doctor reports FastEmbed build support, cache path, model, dimension, current/stale/missing/failed embedding counts, last reconcile throughput, and the next command needed to make local AI current. reconcile writes model-id, dimension, text-hash-bound, and embedding-input-hash-bound chunk embeddings in configurable batches for eligible current chunks only. semantic_search combines BM25 candidates, vector similarity, symbol/name/path boosts, graph-neighborhood boosts, and optional git/tracker papertrail boosts. Embeddings are used only when the active model is installed, the embedding dimension matches active model metadata, the artifact status is Current, the artifact text hash matches the current chunk text hash, and the stored embedding input hash matches the current bounded embedding input; stale embeddings are treated as absent. There is no summarizer or LLM runtime in this milestone.

Reconcile intentionally does not embed every chunk. It skips generated/coarse chunks, tiny chunks, oversized low-value chunks, unsupported languages, low-signal import/export barrels, and test fixtures with explicit policy reasons such as SkipGenerated, SkipTooSmall, SkipTooLarge, SkipLowSignal, SkipLanguageUnsupported, and SkipTestFixture. Source symbols and docs are prioritized ahead of tests and low-signal chunks. rag-rat reconcile --plan reports skipped counts by policy and missing work by priority; reconcile JSON reports batch size, input character counts, truncated input counts, chunks_per_sec, chars_per_sec, and average input size. Use rag-rat reconcile --changed-first --max-seconds 60 --batch-size 64 for bounded daily catch-up and rag-rat reconcile --until-clean --batch-size 64 for a full backlog pass.

Indexing uses a Rayon worker pool for CPU-heavy preparation: file reads, hashing, tree-sitter chunk/symbol preparation, and git-log parsing run across available cores. Reconcile builds bounded embedding inputs, runs model inference outside SQLite transactions, then writes vectors through one short serialized transaction per batch.

Parser failures are visible through index_status.parser_failure_paths, with path, language, and message for each failed source parse. Markdown files are chunked by headings instead of parsed with tree-sitter.

Orientation & ranking

repo_brief, repo_clusters, and important_symbols answer “what is this repo, and what holds it together?” before you know a symbol to look up.

repo_brief ranks files by mode: spine (central coupling — what the rest of the code depends on), churn (git change frequency), god_modules (oversized high-fan-in files), or refactor_candidates. Each row carries size/coupling/churn/memory signals and a suggested next tool. repo_clusters groups the repo into ownership clusters from path proximity, graph edges, and git co-touch, with a representative file per cluster (min_cluster_size sets the floor). Both accept the orientation include array (memories on by default, generated off).

important_symbols ranks the most load-bearing symbols by weighted PageRank over the call/type/import edge graph — SCIP-aware once rag-rat oracle run has populated compiler edges. With no personalize, it auto-seeds from your current git diff and returns importance relative to your current changes (the random surfer teleports back to what you’re editing, lifting the spine those symbols depend on). Pass personalize (a list of names, path::name refs, or sym_<hex> handles) to seed it explicitly, or a single "global" to force whole-repo PageRank. The result is labeled: mode (which scale), seed_source (seed provenance), and symbols. See oracle.md for how the oracle sharpens the ranking.

Clones

find_clones returns candidate clone classes ranked by refactor ROI (cross-module spread × member count × token length × load-bearing factor × cohesion), each with a completeness provenance block. min_similarity (if set) must be in [0.5, 1.0] (θ default 0.7); min_copies defaults to 2. A limited query (limit: N) is capped at the refine budget (currently 50): it returns at most 50 classes, all refined with anti-unification metrics; omit limit (or pass null) to retrieve every class with only the top 50 refined. completeness.refine_budget_clamped is true when a supplied limit hit that cap. The candidate graph is precomputed in the background, so the tool stays responsive on large repos.

clones_for_symbol returns the one clone class containing a specific symbol — selected by exactly one of id (a sym_<hex> handle), ref (path::name), or path + line together. It returns the candidate class if the symbol is fingerprinted and has clone siblings, or null if it is unique or not fingerprinted. This is the same signal the Write/Edit clone-check hook uses at write time.

Repo memories

Memory tools read and write the durable, source-anchored notes described in the README. memory_create records one (typed kind, title, body, confidence), memory_search finds them by keyword, and memory_for_symbol / memory_for_path / memory_for_call_path fetch the full memories bound to an anchor (the expanded form of the compact headers impact_surface attaches). memory_show expands one memory to its full body by memory_id — the expand path for a [memory] surface = "summary" compact attachment.

memory_update edits text/status/confidence/kind/tags; memory_mark_obsolete retires a memory (kept for audit, hidden from active recall). memory_validate re-anchors every memory against current source and marks each current / relocated / stale / gone / pending (it also runs automatically after indexing); memory_doctor lists the stale/gone/pending ones with suggested re-anchor targets. Rebind a moved memory with memory_rebind rather than obsoleting and recreating it — a pending anchor is alive on an in-flight worktree branch and re-anchors itself when that branch lands, so leave those alone.

BindTarget (the bind on memory_create / memory_rebind) names exactly one anchor form: a logical symbol (id), a concrete chunk_id, a graph edge_id, a path (optionally with start_line + end_line), a commit_hash, a tracker-item ref (tracker + project + item_key), a call-path (edge_path: an ordered list of edge ids, from which the server derives the authoritative edge_sequence_hash), or a directory (dir, relative to the repo root; "" anchors to the root). On memory_create only, omit bind entirely to create an unanchored node — a Concept or standalone Task that lives only as a graph node. memory_rebind requires a real target: it rejects an empty bind (to unanchor an existing memory, delete and recreate it).

Memory graph edges. Memories are not only a flat list — they form a typed graph. memory_edge_add connects a source node to another node or a GitHub issue with a relation: depends_on (a task DAG), relates_to (a mind-map link), supersedes, derived_from, or tracks (issue ← task). Give exactly one target: a target_node_id (with an optional target_repo_id for a cross-repo edge) or a full GitHub ref. memory_edges lists a node’s edges — direction: "from" for its outgoing edges (dependencies / links / tracks), direction: "into" for the reverse traversal (e.g. the tasks that tracks an issue, or the nodes that depend on this one). memory_edge_remove deletes an edge by its stable edge_key.

Dream — memory maintenance

dream is the pull surface for keeping the memory layer honest as the code moves under it. It recomputes a deterministic maintenance worklist on each call and returns it ranked, each finding with a stable id:

  • coverage gaps — load-bearing symbols (by the same PageRank as important_symbols) that carry no memory, so the next agent editing them gets nothing.
  • stale references — a memory citing a path or anchor that no longer resolves.
  • model-verdict findings (e.g. memory_divergence) that a prior opt-in model pass persisted — see below.

dream is classified as a write tool (like rag-rat dream, it syncs the dream_findings table) even though it returns a worklist. It runs the deterministic findings only; it does not run the optional model passes — those stay on the CLI/cron rag-rat dream --verify (recompute each memory’s verdict against current source reality) and --compact (rewrite a memory to a tighter summary), which run a small model on an ephemeral remote GPU ([llm.dream.remote]) only when work is pending. Findings those passes persist still surface through dream.

dream_review applies a human (or strong-agent) verdict to one finding by id — a full id or an unambiguous git-style prefix — as accept (a real gap to act on), dismiss (noise), or reset (clear a prior verdict, back to the open worklist). Verdicts survive future dream runs, so the worklist burns down instead of re-proposing settled findings. Pass all: true to dream to also list the already accepted / dismissed findings (for a reviewer to see and reset). This mirrors the CLI rag-rat dream <id> --accept|--dismiss|--reset.

Source: docs/mcp-tools.md · synced from cq27-dev/rag-rat