"Your agent just rebuilt utils/retry.ts. Again."
Without structural awareness, agents can't find what already exists. They write new code instead of using yours.
Agents re-implement what already exists, call functions with the wrong signatures, and miss the right abstraction — because they're navigating blind. Satori is an MCP server that gives agents exact, structurally-aware access to your code. Ask in natural language. Get back the exact symbol.
Add this block to your agent's MCP config. That's it.
[mcp_servers.satori]
command = "npx"
args = ["-y", "--prefer-offline", "@zokizuan/satori-mcp@3.7.0"]
startup_timeout_ms = 180000
env = { EMBEDDING_PROVIDER = "VoyageAI", EMBEDDING_MODEL = "voyage-4-large", EMBEDDING_OUTPUT_DIMENSION = "1024", VOYAGEAI_API_KEY = "your-api-key", VOYAGEAI_RERANKER_MODEL = "rerank-2.5", MILVUS_ADDRESS = "your-milvus-endpoint", MILVUS_TOKEN = "your-milvus-token" }
"satori": {
"type": "local",
"command": [
"npx",
"-y",
"--prefer-offline",
"@zokizuan/satori-mcp@3.7.0"
],
"enabled": true,
"timeout": 30000,
"environment": {
"MILVUS_ADDRESS": "your-milvus-endpoint",
"MILVUS_TOKEN": "your-milvus-token",
"VOYAGEAI_API_KEY": "your-api-key",
"EMBEDDING_PROVIDER": "VoyageAI",
"EMBEDDING_MODEL": "voyage-4-large",
"EMBEDDING_OUTPUT_DIMENSION": "1024",
"VOYAGEAI_RERANKER_MODEL": "rerank-2.5"
}
}
grep doesn't understand behavior. Generic RAG gives context-free chunks. Neither knows your call graph, your symbol map, or what changed ten minutes ago.
Without structural awareness, agents can't find what already exists. They write new code instead of using yours.
Stale or missing context means wrong calls. Every time an agent works from a hallucinated understanding of your API surface, it produces broken code.
Generic retrieval doesn't understand runtime vs test noise. You get contaminated context, and the agent builds on it.
Without cited sources and exact line spans, you can't audit AI decisions. You're reviewing changes you can't trace.
Satori exposes exactly six MCP tools. The north-star workflow gets from question to edit-ready context in four deterministic, cited steps.
search_codebase({
path: "/repo",
query: "auth token refresh flow",
scope: "runtime",
resultMode: "grouped",
groupBy: "symbol",
limit: 5
})
Semantic search over runtime code only. Returns symbol-grouped
results with a callGraphHint ready for the next
tool.
call_graph({
path: "/repo",
symbolRef: <from callGraphHint>,
direction: "both",
depth: 2
})
Callers + callees, depth-bounded. Before editing, the agent understands blast radius.
file_outline({
path: "/repo",
file: "src/auth/session.ts",
limitSymbols: 50
})
Deterministic symbol map for the file. Use
resolveMode=exact to jump by label or ID.
read_file({
path: "/repo",
open_symbol: "refreshToken",
mode: "annotated"
})
Exact symbol span opens. Annotated mode returns content + outline status in one call.
Most retrieval tools work in demos. Satori is designed for repos that are changing, messy, and real.
Stat-first hash-on-change. Only files that actually changed get re-embedded. Most syncs are seconds.
rankingMode=auto_changed_first boosts recently-changed
files so results match what you’re working on now.
scope=runtime excludes tests/fixtures/docs from code
search. docs targets documentation only.
mixed includes everything.
When noise dominates top results,
hints.noiseMitigation tells you exactly what to add
to .satoriignore. Wait one debounce window (default
5s). Done.
Incompatible index states are blocked with a deterministic
requires_reindex envelope — no silent stale reads.
If a file is temporarily unreadable, prior state is preserved. No false removals, no destructive churn.
Determinism is a design contract, not an aspiration. Every comparator, tie-break chain, group selection, and warning is explicit in code and verified in a regression matrix.
Deterministic tie-break chain:
score desc → file asc → start_line asc → symbol label asc → symbol id asc
Deterministic retrieval makes agent behavior reproducible: when something breaks, you can replay it. When something works, you can rely on it repeating.
The whole API footprint is six MCP tools — minimal routing, maximal auditability. Perfect clarity.
list_codebasesShows tracked repos grouped by state (Ready / Indexing / Requires Reindex / Failed). Start every session here.
manage_indexCreate, reindex, sync, status, clear. First-time setup, recovery, or manual freshness triggers.
search_codebasePrimary retrieval: scope + grouping + ranking + sync-on-read freshness. The tool you call most.
file_outline
Deterministic symbol map + exact resolver
(resolveMode=exact). Use before editing.
call_graphCallers/callees traversal, depth-bounded. Know blast radius before touching critical code.
read_file
Content + exact spans + open_symbol + annotated
envelope. Final step: open only what matters.
When things go wrong, the tool tells you exactly what to do next — no guesswork.
Any requires_reindex response includes
hints.reindex with the exact command to run:
manage_index({ action: "reindex", path: <hints.reindex.args.path> })
// then retry the original tool call
If hints.noiseMitigation appears, apply the
suggested .satoriignore patterns, wait one debounce
window (default 5s), then rerun search.
If call_graph returns not_ready /
missing sidecar, reindex. While waiting, every search result
includes navigationFallback (executable readSpan +
optional fileOutlineWindow).
Run search_codebase with the filename in the query
and scope: docs or scope: mixed, then
pivot to the canonical path.
Zilliz free tier limits collections.
manage_index create returns guided text naming a
collection to drop. Retry with zillizDropCollection.
Start with the minimum. Open details only when you need them.
[mcp_servers.satori]
command = "npx"
args = ["-y", "--prefer-offline", "@zokizuan/satori-mcp@3.7.0"]
startup_timeout_ms = 180000
env = { EMBEDDING_PROVIDER = "VoyageAI", EMBEDDING_MODEL = "voyage-4-large", EMBEDDING_OUTPUT_DIMENSION = "1024", VOYAGEAI_API_KEY = "your-api-key", VOYAGEAI_RERANKER_MODEL = "rerank-2.5", MILVUS_ADDRESS = "your-milvus-endpoint", MILVUS_TOKEN = "your-milvus-token" }
"satori": {
"type": "local",
"command": [
"npx",
"-y",
"--prefer-offline",
"@zokizuan/satori-mcp@3.7.0"
],
"enabled": true,
"timeout": 30000,
"environment": {
"MILVUS_ADDRESS": "your-milvus-endpoint",
"MILVUS_TOKEN": "your-milvus-token",
"VOYAGEAI_API_KEY": "your-api-key",
"EMBEDDING_PROVIDER": "VoyageAI",
"EMBEDDING_MODEL": "voyage-4-large",
"EMBEDDING_OUTPUT_DIMENSION": "1024",
"VOYAGEAI_RERANKER_MODEL": "rerank-2.5"
}
}
# Runtime-first grouped search (default mode)
satori.search_codebase({"path":"/repo","query":"auth flow","scope":"runtime","resultMode":"grouped","groupBy":"symbol","limit":10})
# Docs-only investigation
satori.search_codebase({"path":"/repo","query":"migration guide","scope":"docs","resultMode":"raw","limit":10})
# File-level grouping + debug traces
satori.search_codebase({"path":"/repo","query":"retry policy","scope":"mixed","resultMode":"grouped","groupBy":"file","limit":12,"debug":true})
# Call graph jump from grouped result hint
satori.call_graph({"path":"/repo","symbolRef":{"file":"src/auth.ts","symbolId":"sym_auth_refresh"},"direction":"both","depth":2,"limit":20})
Grouped mode returns freshness + symbol metadata and a
discriminated callGraphHint (`supported: true|false`).
Called satori.file_outline({"path":"/home/hamza/repo/tradingview_ratio","file":"src/auth/session.ts","limitSymbols":50})
Called satori.read_file({"path":"/home/hamza/repo/tradingview_ratio/docs/CLI_COMMANDS.md","start_line":1,"end_line":220,"mode":"annotated"})
Result: "QAP CLI Command Reference ..."
Called satori.search_codebase({"path":"/home/hamza/repo/tradingview_ratio","query":"MASTER_AUDIT.md location current path archived","scope":"docs","resultMode":"raw","limit":10})
Hit: docs/research/PHASES_OVERVIEW.md:322-371
Called satori.read_file({"path":"/home/hamza/repo/tradingview_ratio/archive/docs/northstar/core/INVARIANTS.md","start_line":1,"end_line":220,"mode":"annotated"})
Error: File not found
Fallback:
satori.search_codebase({"path":"/home/hamza/repo/tradingview_ratio","query":"northstar/core/INVARIANTS.md docs/INVARIANTS.md reference mismatch","scope":"docs","resultMode":"raw","limit":10})
satori.read_file({"path":"/home/hamza/repo/tradingview_ratio/docs/INVARIANTS.md","start_line":480,"end_line":620,"mode":"annotated"})
Infinite Void