{
  "markdown": "# slimdex-mcp\n\n[![npm](https://img.shields.io/npm/v/slimdex-mcp?color=cb3837&logo=npm)](https://www.npmjs.com/package/slimdex-mcp)\n[![MCP Registry](https://img.shields.io/badge/MCP%20Registry-listed-0a7ea4)](https://registry.modelcontextprotocol.io)\n[![Glama score](https://glama.ai/mcp/servers/Siddhukaushik/slimdex-mcp/badges/score.svg)](https://glama.ai/mcp/servers/Siddhukaushik/slimdex-mcp)\n[![license](https://img.shields.io/npm/l/slimdex-mcp)](LICENSE)\n\n**Your agent reads a 900-line file to change one function — then pays for that\nfile again on every turn that follows.** The whole conversation is re-sent each\ntime, so an early read isn't a one-time cost. It's rent.\n\nSlimdex is a local [MCP](https://modelcontextprotocol.io) server that gives\ncoding agents **narrow retrieval** instead: a file's outline, one symbol's body,\nwho calls it, what breaks if it changes — and memory that survives the session,\nso the next chat starts informed rather than re-deriving the repo from zero.\n\n```bash\nclaude mcp add slimdex -- npx -y slimdex-mcp\n```\n\n**~50% fewer tokens** in day-to-day use — ~55–60% on navigation-heavy work,\n~45% on output-heavy work, and 85–90% on the worst case it was built for\n(one 6,200-line file, explored through a skeleton and 12 symbol bodies instead\nof four full reads).\n\n> **Status: 1.1.0, on [npm](https://www.npmjs.com/package/slimdex-mcp) and in the\n> [MCP Registry](https://registry.modelcontextprotocol.io).** Those numbers are\n> self-measured on the repos it has been run against, single sessions, not\n> independently validated — and `stats` counts characters, not tokens.\n> Read [What's actually verified](#whats-actually-verified) before relying on it.\n\n| Tool | What it returns |\n|------|-----------------|\n| `index_repo` | Builds/refreshes a persistent symbol + import index; only changed files re-parse |\n| `outline_file` | Declarations of one file with line numbers |\n| `get_file_skeleton` | Signatures with bodies elided, nesting preserved |\n| `read_lines` | One line range |\n| `get_symbol_context` | One function/class body ±2 lines, capped by `maxLines`; `names:[...]` pulls several bodies in one call |\n| `search_code` | `path:line:col` + the matching line with caret highlight; `limit`/`offset`/cursor pagination |\n| `find_definition` | Definition site(s) of a symbol as `path:line:col` |\n| `search_symbols` | Fuzzy symbol-name lookup, ranked exact→prefix→substring→subsequence |\n| `search_intent` | Natural-language query ranked over symbols by BM25 (no embeddings) — find code by what it does |\n| `context_pack` | One call: ranks a topic's symbols, shows how they connect, and bundles the top bodies under a budget — the whole exploration in one round-trip |\n| `find_references` | Textual references as `path:line:col` + enclosing function |\n| `find_tests` | Of the references to a symbol, which live in test files — or a warning that none do |\n| `replace_symbol` | Overwrite a symbol's body addressed by name (no re-sent old code); snapshots first, re-indexes after |\n| `get_context` | One call: opt-in definition / signature / callers / imports / dependents, budgeted |\n| `repo_map` | Dir-level file/line/symbol counts; `path:` drills into a dir's largest files |\n| `changed_files` | Changed files + which symbols each hunk lands in |\n| `dep_graph` | `imports` / `dependents` / a Mermaid diagram (`root`+`depth` BFS) |\n| `stats` | Per-tool call counts and response sizes, in characters, plus read follow-through and write discipline |\n| `batch` | Runs several calls in one request |\n| `recap` | Prior sessions' activity, reconstructed automatically from the server's tool-call journal — works even when nothing was saved |\n| `brief` | One-shot session opener: repo summary + journal-derived focus + saved conclusions checked against the live index (✓ live / ⚠ maybe stale) |\n| `digest_save` / `digest_get` | Store a compact repo architecture cheat-sheet once; read it back with a per-covered-file freshness verdict, so the next session skips re-exploring |\n| `snapshot` | Copies uncommitted files into `.slimdex/snapshots/` (also auto-runs hourly via `index_repo` on a dirty tree) — insurance against accidental resets, not a substitute for committing |\n| `memory_save/search/list/delete` | Durable notes in `.slimdex/memory.json` |\n\nThe retrieval guidance below also ships in the server's MCP `instructions`, so\nclients inject it into the model's context automatically.\n\n### Recommended agent flow\n\n`brief` first, at the very start of a session — one call that reports what the\nrepo is, where recent sessions were digging, and which saved conclusions still\nmatch the code (stale ones flagged), so a fresh chat starts informed instead of\nblank. Then `get_context(\"Foo\")` to answer \"what is this, who calls it, what does\nit depend on\" in one response. To understand a whole *area* rather than one\nsymbol, `context_pack(\"how does auth work\")` runs the entire exploration\nserver-side and hands back a single bounded bundle — the relevant symbols, how\nthey connect, and the top bodies — so you spend one call and one transcript\nentry instead of ten. Don't know the name, only what it does? —\n`search_intent(\"parse the config file\")` ranks symbols by intent with BM25, no\nembeddings. Drop to `get_symbol_context` for one body (it flags itself if the file\ndrifted from the index, so you don't re-read to check), `get_file_skeleton` for a\nfile's shape, and `read_lines` when you need exact source. Before editing a\nsymbol, `find_tests` on it to see what covers it; to\nrewrite a whole function, `replace_symbol` (you send only the new body — the old\ncode isn't re-sent just to locate the edit). Use `batch` to bundle several\nlookups. Every search tool takes `limit` (default 20) and `offset`.\n\n**Response budgeting:** `get_context` sections are opt-in via `include`\n(default: definition, signature, callers, imports — add `body` or `dependents`\nexplicitly), callers are capped by `callerLimit`, and the response is bounded\nby `maxChars` (default 12,000). Every cap that trips prints an explicit notice\n(`showing 3 of 68`, `truncated at maxChars=...`) rather than dropping data\nsilently. `get_symbol_context` caps its span with `maxLines` the same way, and\n`memory_list` returns the newest 50 facts unless told otherwise, as ~150-char\npreviews rather than whole bodies (`memory_get ids:[...]` expands them,\n`full:true` dumps everything). On an 18-fact store that is the difference\nbetween ~4,100 and ~18,600 chars in the call every session opens with.\n\n### Config: `<root>/.slimdex.json` (optional)\n\n```json\n{\n  \"ignoreDirs\": [\"fixtures\", \"backend/src/main/resources/static/assets\"],\n  \"extensions\": [\".astro\", \".vue\"],\n  \"suffixes\": [\".stories.mdx\"],\n  \"exclude\": [\"generated/\", \"legacy/vendor\"],\n  \"maxFileBytes\": 2000000\n}\n```\n\n`suffixes` matches a filename ending, for file types an extension can't identify.\nSalesforce metadata sidecars ship as a built-in: `AccountSvc.cls-meta.xml`,\n`panel.js-meta.xml` and `Account.object-meta.xml` are indexed, while `pom.xml`,\n`web.xml` and `manifest/package.xml` are not — adding `.xml` to `extensions`\nwould have pulled in every config tree in the repo. Suffix-matched files are\nindexed for search and read reach, not symbols.\n\nMerged on top of the built-in ignore list (`node_modules`, `dist`, `.venv`,\n`.svelte-kit`, `Pods`, `.pytest_cache`, …). An `ignoreDirs` entry is either a bare\nname, matching any directory so called at any depth, or a path containing `/`,\nanchored at the repo root and respecting directory boundaries (`src/gen` will not\nalso ignore `src/generated`). `index_repo` echoes what it loaded and warns about\nunknown keys, wrong types, or invalid JSON, so a typo'd config isn't silently\nindistinguishable from none.\n\n**Build output usually needs no config at all.** Beyond the directory list, any\nfile whose lines run past ~5,000 characters is treated as minified build output and\nleft out of the index — bundlers strip newlines, and hand-written source doesn't\nlook like that. This catches what a name list structurally cannot: a hash-named\nbundle (`index-B7xK2p9q.js`) inside a directory called `assets`. `assets`, `public`\nand `static` are deliberately *not* ignored by name, because real source lives in\nthem; `index_repo` reports the count as `skipped(minified build output): N`.\n\n### How the token saving works\n\nThere's no compression trick. The saving is behavioral: these tools let an agent\nretrieve outlines, ranges, and locations instead of whole files, and the\npersistent index means repeat lookups hit a cached query rather than a re-read.\n\nTwo later sessions, run by different models on different repo shapes, added\nreal-world numbers to the original report:\n\n**Multi-file web app, bug-fix session (GPT-5.3-Codex).**\n19 credits reported with slimdex; the model's own estimate for the same scope\nwithout it: 45–70 credits. Math: 19/45 → 19/70 ≈ **58–73% cheaper**. The\ncounterfactual is the model's estimate, not a measured A/B — directional.\n\n**Single giant file (folio-app: one 6,200-line, 313 KB `app.js`).**\nSlimdex's own stats: ~34,000 chars across 8 calls ≈ 9–10k tokens — one\nskeleton (213 signatures), then bodies of only ~12 relevant functions, 9 of\nthem fetched in a single `get_symbol_context names:[...]` call. The naive\npath: 313 KB ≈ 78–85k tokens across 3–4 forced full reads. Math: ~10k vs\n~80k ≈ **~70k tokens saved, an 85–90% reduction** on exploration. The bug's\ndiagnosis (an export path with no matching import path) was visible from the\nskeleton's signatures before a single body was opened.\n\nTogether they sketch the scaling law: **the saving scales with how much\nirrelevant code the naive path would drag in.** One giant file is the best\ncase; a normal repo lands around half to two-thirds cheaper; a repo of tiny\nfiles breaks even. Same standing caveats as everything here: stats count\nchars, not tokens (÷3.5–4), and single sessions are evidence, not benchmarks.\n\n**Both figures above measure reading only, which is the cheaper half.** Output\ncosts roughly 4–5× input, so an undisciplined edit wastes more than an\nundisciplined read: rewriting a whole function through a generic edit tool means\nre-sending the entire old body purely so the tool can locate it. `replace_symbol`\naddresses by name and that cost disappears. `stats` reports this alongside\nfollow-through, because the leak is otherwise invisible — the expensive path\nstill produces a correct edit, so nothing signals that you overpaid:\n\n```\nwrite discipline:\n  replace_symbol: 0 call(s), 0 symbol(s) rewritten by name\n  changed outside slimdex: 12 file(s)\n  pre-edit checks (find_tests/dep_graph/get_context/changed_files): 0\n```\n\nExternal edits are inferred from content hashes moving between two `index_repo`\nruns, so the number is honest about its limits: it sees that bytes changed, never\nwhich tool changed them, and a human editing in another window counts too.\n\n\n### The realistic whole-workflow band\n\nThe figures above are single-scenario *exploration* numbers — the best case,\nwhere the naive path would have dragged in the most irrelevant code. Averaged\nacross a whole real workday, not just the exploration slice, the band settles\nlower:\n\n- **~55–60%** on navigation-heavy work — reading and understanding a codebase,\n  where narrow retrieval replaces whole-file reads most often.\n- **~45%** on output-heavy work — churning out new code, where more of the cost\n  is generation the server doesn't touch (though `replace_symbol` now shaves the\n  write side too).\n- **~50% averaged** over regular day-to-day use. The saving compounds the more\n  sessions run through it, because `brief` and memory mean each new chat starts\n  informed instead of re-deriving the repo from zero.\n\nUse it regularly across sessions in your IDE for the best of this.\n\n**Treat these as one data point, not a benchmark.** Single repo, single task, one\nA/B run each, self-measured, no repetitions or variance. Your mileage depends\nheavily on whether your agent actually reaches for the narrow tools instead of\nfalling back to reading files — which varies by client and model. The method is\nrepeatable if you want to check it: run the same task in two fresh sessions, one\ninstructed to use only Slimdex and one instructed to avoid it, and compare\n`/status` cache-write.\n\n---\n\n## What's actually verified\n\nBeing explicit, since the rest of this README is easy to over-read.\n\n**Covered by the unit suite** (`npm test` runs 224 tests across 23 files):\n\n- Symbol extraction across JS/TS (incl. class and object-literal methods),\n  Python, Go, Rust, Java/C#, and comment skipping — `symbols.test.ts`\n- Import extraction for JS `import`/`require`/`export-from`, Python, Rust\n- Block extraction, brace-scoped and indentation-scoped, with string/comment\n  awareness (quotes, templates, `//`, `/* */`, full-line `#`) — `extractBlock.test.ts`\n- Import resolution, external-module classification, reverse-edge dependents,\n  Mermaid emission, and root-BFS depth scoping — `graph.test.ts`\n- Search match format, pagination without overlap, per-line occurrence counting,\n  exact totals, regex escaping/rejection — `search.test.ts`\n- Opaque cursor round-tripping and malformed-cursor rejection; parser-backend\n  fallback — `pagination.test.ts`\n- Outline declaration detection vs. control flow — `outline.test.ts`\n- `get_symbol_context` `maxLines` budgeting and truncation notice\n\n- String/comment masking and brace-depth tracking — `lexer.test.ts`\n- Per-language extraction for all twelve supported languages — `languages.test.ts`\n- The index cache returns the same object until the index is rewritten\n- `.slimdex.json` loading: every key applied through a real index build, plus\n  the failure modes (invalid JSON, unknown keys, wrong types) each producing a\n  visible warning instead of silence — `config.test.ts`\n- `changed_files` against a real temporary git repository: hunk→symbol\n  attribution, untracked files, explicit base refs, and formatting; skips\n  cleanly when git isn't installed — `git.test.ts`\n- The file watcher, with real fs events: a save is debounced, reindexed, and\n  lands in the on-disk index — `watch.test.ts`\n- Graph edges beyond imports: name-reference edges for import-less code\n  (class→used-class, interface→implementation via dependents, trigger→handler)\n  and declarative-wiring edges from repo XML (metadata-binding→class), with\n  comment/string mentions excluded and per-build caching — `apexgraph.test.ts`\n- The in-memory file cache serves repeats without re-reading and always serves\n  fresh content after an on-disk change — `fscache.test.ts`\n- Test-file detection across JS/TS/Python/Go/Ruby/Java/C# conventions, with\n  Windows separators normalized and ordinary source (`latest.ts`, `Contest.java`)\n  not misflagged — `testlink.test.ts`\n- The write side: replacing a symbol's block, trailing code preserved, and CRLF\n  vs LF line endings kept so an edit isn't reflowed into a whole-file diff —\n  `edit.test.ts`\n- Memory staleness: a fact is marked live when it names a symbol/file that still\n  exists, flagged stale only when every code mention is gone, and left unflagged\n  for prose — plus brief composition — `brief.test.ts`\n- Intent search: camelCase/snake_case tokenization, and BM25 ranking that surfaces\n  a differently-named symbol by its intent words while scoring an unrelated query\n  to nothing — `intent.test.ts`\n- Freshness: a file newer than its indexed mtime reads as stale (line numbers may\n  be off), a matching mtime reads as fresh, and a missing file never cries stale —\n  `freshness.test.ts`\n- `context_pack` assembly: header + ranked symbols + bodies in one bundle, the\n  no-match message, char-budget gating that still guarantees the first body, and\n  the symbols-limit cap — `pack.test.ts`\n- The architecture digest: covered files modified after the digest read as stale,\n  a newer digest reads clean, coverage-scope and directory-prefix filtering, and\n  the rendered fresh/stale verdict — `digest.test.ts`\n\n**Covered end to end, through the real MCP server** (`integration.test.ts` spawns\nthe server over stdio against a temporary fixture repo and asserts on output):\n`index_repo`, `repo_map`, `read_lines`, `get_file_skeleton`, `outline_file`,\n`get_symbol_context`, `find_definition`, `find_references`, `find_tests` (the hit\nand the no-coverage warning), `search_intent` (intent ranking), `context_pack` (one-call\nbundle), `digest_save`/`digest_get` (round trip with freshness verdict),\n`get_context` (including its `maxChars` cap),\n`dep_graph` (imports + mermaid), `batch`, `search_code`, `search_symbols`,\n`stats`, `brief`, `replace_symbol` (write-then-query round trip and the\nunknown-symbol refusal), the `memory_save/search/list/delete` round trip, the\npath-escape guard, and the not-found paths.\n\nCI runs the build and both suites on Ubuntu + Windows, Node 20 and 22.\n\n**Caveat on the watcher test:** recursive `fs.watch` is platform-dependent, so\n`watch.test.ts` degrades to a logged skip on filesystems that never deliver an\nevent — same behavior as the watcher itself. On Windows, macOS, and current\nLinux it asserts the full save→reindex path.\n\n`npm run smoke` still exists but proves only that the pipeline is alive — the\ncorrectness assertions live in `integration.test.ts`.\n\n**Verified by inspection:** `src/` contains no network calls — no code leaves\nyour machine. This one you can check yourself:\n`grep -rE \"fetch\\(|https?://|axios|http\\.request\" src/`.\n\n## Longer documentation\n\nIn [`docs/`](docs/):\n\n- [`tool-guide.md`](docs/tool-guide.md) — every tool explained twice\n  (technically and in plain words) with an example each, the combined\n  workflow, and how mtime-based persistence works\n- [`tool-guide.html`](docs/tool-guide.html) — the same guide as a styled,\n  self-contained page for the browser\n- [`token-savings-report.md`](docs/token-savings-report.md) — the original A/B\n  measurement, its method, and how to repeat it\n- [`agent-brain.md`](docs/agent-brain.md) — the full operating discipline as a\n  readable document\n- [`agent-brain-slim.md`](docs/agent-brain-slim.md) — **the one to drop into a\n  repo** as CLAUDE.md / AGENTS.md. Self-contained and one page: savings ladder,\n  question→tool table, memory discipline, session hygiene, honest limits, env\n  knobs. Same coverage as the full document at ~30% of the prose, because the\n  tool rules are dense tables rather than paragraphs the server already injects.\n\n## Language coverage\n\nTwo measurements, because fixtures alone prove very little.\n\n**Fixtures** — one per language, counting the declarations a developer would\nactually navigate to: **65/65 found, 0 false positives**, pinned by\n`test/languages.test.ts`.\n\n**Real third-party code** — extraction run over ~11,800 files from several\nhundred real packages (React, Babel, Remix, Socket.io, Playwright, Three.js,\nEmotion, zod, ajv …) and compared against an independently written heuristic for\nwhat counts as a declaration: **95.9% recall**. Reproduce it yourself:\n\n```bash\nnpm run audit -- ./node_modules            # or any directory of code you didn't write\n```\n\nThat number is a floor, not a grade — the truth heuristic counts some\nnon-declarations, so real recall is a little higher. What it's for is catching\nregressions and finding the next real gap.\n\n### About frameworks\n\nAlmost nothing that failed the audit was framework-specific. Frameworks add\nannotations, decorators and conventions; they rarely invent syntax. Handle the\nlanguage and the frameworks come with it — fflib's Application/Domain/Selector/\nService/UnitOfWork layers extract completely (129 declarations) without a single\nfflib-aware rule.\n\nThe one genuine exception is **test DSLs**. A vitest/jest/mocha/RSpec file often\nhas no top-level declarations at all, so entire test directories used to index to\nnothing. `describe`/`it`/`test` titles are now indexed as kind `test`, which is\nwhat you actually navigate to in a test file.\n\nFramework **semantics** are recovered wherever the reference exists somewhere\nin the repo, through two extra edge sources in the graph:\n\n- **Name-reference edges**, for languages that have no import statement (e.g.\n  Apex): if one file's code — comments and strings masked out — mentions a\n  top-level type defined in another file, that's an edge. This is what makes\n  `implements` answerable as \"who implements this interface\", and links a\n  trigger to the handler class it news up.\n- **Declarative-wiring edges**: bindings that frameworks keep in configuration\n  rather than code (custom-metadata records, flow definitions) usually live in\n  the repo as XML with the type name as an element value. Repo XML is scanned\n  for known type names — XML comments excluded — and each hit becomes a\n  `metadata-file → class` edge, so `dependents` answers \"what wires this up\".\n\nBoth scans are cached per index build and cost nothing on repos without such\nfiles. Pinned by `apexgraph.test.ts`. What no static reader can see is a\nbinding that exists **only in a live system** — configured in a running org or\ndatabase and never retrieved into the repo. If it's not in the repo in any\nform, there is no edge to draw; search the type name instead.\n\n| Language | Extensions | What's recognised |\n|---|---|---|\n| JavaScript / TypeScript | `.js .jsx .mjs .cjs .ts .tsx .vue .svelte` | classes, interfaces, types, enums, functions, top-level arrows, class and object-literal methods |\n| Apex | `.cls .trigger` | classes, inner classes, methods (incl. `@AuraEnabled`, `global`, generic returns), triggers |\n| Java | `.java` | classes, interfaces, enums, methods, generic methods with a leading `<T>` |\n| C# | `.cs` | classes, interfaces, structs, async and generic methods, virtual members |\n| Kotlin | `.kt` | classes, data classes, interfaces, `object`, `fun`, `suspend fun` |\n| Swift | `.swift` | classes, structs, enums, protocols, `func`, `static func` |\n| Python | `.py` | classes, `def`, `async def`, dunder and decorated methods |\n| Go | `.go` | funcs, receiver methods, struct and interface types |\n| Rust | `.rs` | structs, enums, traits, `fn`, `pub async fn`, impl methods |\n| Ruby | `.rb` | classes, modules, `def`, `def self.x`, `attr_accessor/reader/writer` |\n| PHP | `.php` | classes, interfaces, traits, methods, functions |\n| Scala | `.scala` | classes, case classes, traits, objects, `def` with modifiers |\n| C / C++ / Objective-C | `.c .h .cpp .hpp .cc .m .mm` | classes, structs, enums, free functions (incl. K&R braces, pointer returns), `Foo::bar` out-of-class definitions, ctors/dtors, namespaces, function-like `#define` macros, `typedef struct {…} Name`, `@interface`/`@implementation`/`@protocol` |\n\n## Performance\n\nCold index is a full parse; warm is an mtime check per file. Measured on Windows,\nNode 24.\n\n| Repo | Files | Symbols | Cold index | Warm index | Typical query |\n|---|---:|---:|---:|---:|---:|\n| Salesforce DX org | 56 | 344 | 0.1 s | 15 ms | < 10 ms |\n| Java + React app | 356 | 1,713 | 0.42 s | 26 ms | 3–57 ms |\n| Synthetic stress | 5,000 | 50,000 | 1.5 s | 0.24 s | 5–22 ms |\n\nThe index is held in memory and invalidated by the index file's mtime. Without\nthat cache every tool call re-read and re-parsed the whole index — about 20 ms of\ndead weight per call on the 5,000-file repo, and it grew with the repo.\n\n`find_references` is the slowest tool at scale because it is a textual scan,\nnot an index lookup — but a literal pre-filter now skips the line-split and\nper-line regex for any file whose raw source doesn't contain the searched name,\nwhich on a typical repo is most of them. Scope with `pathPrefix` to cut the\nremaining file reads when you know roughly where to look.\n\nFile contents are also served from a byte-bounded in-memory LRU (64 MB,\nvalidated by mtime+size per hit), so the second scan of a repo — and the\nskeleton→read_lines→context sequence agents actually perform on one file —\ncosts a `stat()` instead of a read.\n\n## Memory across sessions\n\n`memory_save` writes to `<root>/.slimdex/memory.json`, which outlives the\nprocess — a fact saved in one chat is readable in the next, by a different\nclient, after a restart. Chat and editor share one store only when both point at\nthe same `SLIMDEX_ROOT`.\n\nNothing is captured automatically: the server never sees your conversation, so\nthe agent has to decide what's worth keeping. The shipped `instructions` tell it\nto read memory first in a new session and to save decisions, constraints and\ngotchas as it learns them — but that's guidance to the model, not a guarantee.\n\n## Known limitations\n\n- Symbol extraction is **regex-based and heuristic**, not a parser or LSP. It can\n  miss unusual declarations, and `find_references` is a **textual** match that may\n  include same-named but unrelated identifiers.\n- Symbol and outline extraction now run against a **masked** copy of each line,\n  with string and comment contents blanked out, so declaration-shaped prose\n  inside a template literal is no longer indexed as code. Declarations are also\n  **depth-aware**: a `const x = () => …` or `type X = …` counts only at top\n  level, because locals inside a function body are not things anyone navigates\n  to. Class methods are still indexed at their nesting depth.\n- An *inline* Python `#` comment containing a brace can still confuse block\n  extraction (`#` is also the JS private-field sigil, so it can't be stripped\n  blindly).\n- `changed_files` attributes a hunk to the **nearest preceding declaration** —\n  right for a normal function body, approximate for code between declarations.\n  Treat it as blast radius, not a call graph.\n- `search_code` reports an exact total but stops at an internal scan cap on very\n  large result sets, printing `N+ (scan cap reached)` rather than a confident\n  wrong number.\n- Language support is uneven: JS/TS is the best-covered. C-family and Ruby,\n  formerly the thinnest, gained dedicated rules (free functions, `Foo::bar`\n  definitions, function-like macros, `attr_*`); the remaining soft spots are\n  advanced C++ shapes — templates split across lines, operator overloads.\n- For LSP-grade precision you'd swap the parser for tree-sitter or a language\n  server. `src/parser.ts` is the seam: a `Parser` interface selected by\n  `SLIMDEX_PARSER`, with the regex parser as the only implementation that\n  ships. A tree-sitter backend would drop in there without touching any tool or\n  the index format. It is **not built** — per-language grammars trade away the\n  \"installs instantly, runs offline, zero config\" property.\n\n## Deliberately not built\n\nIdeas evaluated and rejected, with reasoning — these are design opinions, not\nmeasured results:\n\n- **Symbol-ID dictionaries (`S42` → path)** — MCP has no client-side expansion\n  layer, so the model receives an opaque token it must spend another call to\n  resolve.\n- **Token-budget managers / cost estimators** — `chars/4` estimates are\n  unreliable across tokenizers, and auto-compressing on a bad estimate can drop\n  data the model needed.\n- **Delta / \"already-sent, see response #5\" caching** — after context compaction\n  the earlier payload is gone, so the reference resolves to nothing.\n- **Embeddings / semantic search** — large dependency footprint; possible future\n  optional flag, not a default.\n- **A tree-sitter parser backend** — this is the one that would close the\n  remaining ~4%, and it was costed rather than hand-waved: `web-tree-sitter` is\n  WASM so it needs no native compilation, but the grammars\n  (`tree-sitter-wasms`) are **51.7 MB** unpacked against ~4.5 MB for the whole\n  current install. Evaluated and declined at 95.9% measured recall, because\n  \"installs in a second, runs offline, no configuration\" is the property this\n  server exists to have. `src/parser.ts` remains the seam if that calculus ever\n  changes — a backend drops in there without touching a tool or the index\n  format.\n\n---\n\n## Install\n\nPublished on npm as [`slimdex-mcp`](https://www.npmjs.com/package/slimdex-mcp),\nand listed in the [MCP Registry](https://registry.modelcontextprotocol.io) as\n`io.github.Siddhukaushik/slimdex-mcp`. Nothing to build — point your client at:\n\n```bash\nnpx slimdex-mcp\n```\n\nOr from source, if you want to hack on it:\n\n```bash\ngit clone https://github.com/Siddhukaushik/slimdex-mcp\ncd slimdex-mcp\nnpm install\nnpm run build      # produces dist/index.js\nnpm test           # vitest unit suite\n```\n\nVerify it runs end to end against a repo:\n\n```bash\nnpm run smoke                                # this repo\nnode smoke-test.mjs \"C:/path/to/some/repo\"   # any other\n```\n\n### Environment variables\n\n| Var | Effect |\n|-----|--------|\n| `SLIMDEX_ROOT` | Repo to index (or pass as the first CLI arg; defaults to cwd) |\n| `SLIMDEX_WATCH` | Set to `1` to auto-reindex on file save (native watcher, no deps) |\n| `SLIMDEX_PARSER` | Parser backend; only `regex` exists today |\n| `SLIMDEX_PRETTY` | Set to `1` to restore the verbose, human-aligned rendering: longer headers and column padding in `search_code`, `find_definition`, `search_symbols`, `find_references`, `repo_map`, `read_lines`, `outline_file`. Terse is the **default** — that padding is context the model pays for in every later turn. `SLIMDEX_TERSE=0` does the same thing. |\n| `SLIMDEX_PROFILE` | `lean` advertises 15 tools instead of 29, cutting the tool schemas re-sent on every turn from ~22,300 to ~12,600 chars. The other 14 (`get_context`, `changed_files`, `find_tests`, `dep_graph`, `outline_file`, `search_symbols`, `recap`, `memory_list`, `memory_search`, `memory_delete`, `digest_save`, `digest_get`, `snapshot`, `stats`) still work and are called through `batch` — and the server instructions name them under this profile, so the model is told what is batch-only rather than left to discover it. Default `full`. |\n| `SLIMDEX_NO_DEDUPE` | Set to `1` to disable repeat-response suppression (a second identical `read_lines`/`get_file_skeleton`/`outline_file` on an unchanged file answers with a pointer to the earlier call instead of the body; a third identical call re-emits in full). |\n\n## The persistent cache\n\nPer repository, Slimdex writes to `<repo>/.slimdex/`:\n\n- `index.json` — the code index (mtime-invalidated per file, and discarded\n  wholesale when the index format version changes, so a stale index built by an\n  older extractor is never reused)\n- `memory.json` — saved memory facts\n- `stats.json` — per-tool usage counters\n\nThe directory ignores itself: a `*` `.gitignore` is written inside it (the\n`node_modules/.cache` trick), so it never shows up in `git status` and you don't\nhave to touch the repo's own `.gitignore`. Delete that inner file if you *want*\nto commit the cache.\n\n---\n\n## Wiring it into MCP clients\n\nMCP is a shared standard, so the same server should plug into any MCP-capable\nclient. The project root is passed via `SLIMDEX_ROOT` (or as the first CLI\narg).\n\n**Only Claude Code and Claude Desktop have actually been run.** The others below\nare the standard config shape for each client, written from their documented\nformat — they are untested here and may need adjustment.\n\nSince 1.0.0 the simplest wiring is `npx -y slimdex-mcp` — no clone, no build, and\nit stays current. The examples below keep the `node <ABS_PATH>` form for anyone\nrunning from source; to use the published package instead, swap\n`\"command\": \"node\", \"args\": [\"<ABS_PATH>\"]` for\n`\"command\": \"npx\", \"args\": [\"-y\", \"slimdex-mcp\"]`.\n\nReplace `<ABS_PATH>` with your build output, e.g.\n`C:\\path\\to\\slimdex-mcp\\dist\\index.js`, and `<REPO>` with the repo to index.\n\n**No tuning required.** The savings that matter are on by default in every\nclient: memory facts list as previews, responses are terse, an identical re-read\nof an unchanged file answers with a pointer instead of the body, and several\nsymbol edits go in one call. The env vars below are for opting *out*, or for\n`lean` — which trades a further ~8,700 chars/turn against routing a third of the\ntools through `batch`, so it is deliberately not the default.\n\n### Claude Code (CLI) — tested\n```bash\nclaude mcp add slimdex --env SLIMDEX_ROOT=<REPO> -- npx -y slimdex-mcp\n```\nFrom source instead: `-- node <ABS_PATH>`.\n\n### Claude Desktop — tested\n`%APPDATA%\\Claude\\claude_desktop_config.json`\n```json\n{\n  \"mcpServers\": {\n    \"slimdex\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"slimdex-mcp\"],\n      \"env\": { \"SLIMDEX_ROOT\": \"<REPO>\" }\n    }\n  }\n}\n```\n\n### Codex CLI — tested\n`~/.codex/config.toml`\n```toml\n[mcp_servers.slimdex]\ncommand = 'C:\\Program Files\\nodejs\\node.exe'\nargs = ['<ABS_PATH>']\nstartup_timeout_sec = 30\n```\nRegistered globally like this, slimdex attaches to every Codex task and uses\nthat task's working directory as the repo root — no `SLIMDEX_ROOT` needed. Codex\nlaunches the server with a restricted environment, so give `command` an absolute\npath to node rather than relying on `PATH`.\n\n### Cursor — untested\n`.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global)\n```json\n{\n  \"mcpServers\": {\n    \"slimdex\": {\n      \"command\": \"node\",\n      \"args\": [\"<ABS_PATH>\"],\n      \"env\": { \"SLIMDEX_ROOT\": \"${workspaceFolder}\" }\n    }\n  }\n}\n```\n\n### Windsurf — tested\n`~/.codeium/windsurf/mcp_config.json` — same `mcpServers` shape as Cursor.\n\n### VS Code (Copilot / MCP) — tested\n`.vscode/mcp.json`\n```json\n{\n  \"servers\": {\n    \"slimdex\": {\n      \"command\": \"node\",\n      \"args\": [\"<ABS_PATH>\"],\n      \"env\": { \"SLIMDEX_ROOT\": \"${workspaceFolder}\" }\n    }\n  }\n}\n```\n\n### Cline (VS Code extension) — tested\nCline settings → MCP Servers → add:\n```json\n{\n  \"slimdex\": {\n    \"command\": \"node\",\n    \"args\": [\"<ABS_PATH>\"],\n    \"env\": { \"SLIMDEX_ROOT\": \"<REPO>\" }\n  }\n}\n```\n\n### Zed — tested\n`settings.json` → `context_servers`\n```json\n{\n  \"context_servers\": {\n    \"slimdex\": {\n      \"command\": { \"path\": \"node\", \"args\": [\"<ABS_PATH>\"], \"env\": { \"SLIMDEX_ROOT\": \"<REPO>\" } }\n    }\n  }\n}\n```\n\n> For clients that expose the workspace folder (Cursor, VS Code),\n> `${workspaceFolder}` keeps Slimdex pointed at the repo you have open.\n\n## Typical agent workflow\n\n1. `index_repo` once at the start (faster on subsequent runs), then `brief` to\n   pick up where past sessions left off with stale notes already flagged.\n2. `repo_map` → get the lay of the land.\n3. `outline_file` on a file of interest → pick line ranges.\n4. `read_lines` for just those ranges.\n5. `find_definition` / `find_references` / `dep_graph` to navigate.\n6. `find_tests` before editing a symbol; `replace_symbol` to rewrite one without\n   re-sending its old body.\n7. `memory_save` decisions and gotchas so the next session starts informed.\n\n## License\n\nMIT © 2026 Kael VK Inc. (Business Number 751569161 RC0001) — see [LICENSE](LICENSE).\n\nProvided as is, with no warranty and no support. If it doesn't build, doesn't\nrun, or doesn't work on your setup, that's yours to carry — see the disclaimer\nin the license.\n",
  "bytes": 34685,
  "sha": "e15ab7b6585ae99b08c82703bf6b55a71a321cf6d143380684436b83bbcea676",
  "repo_slug": "siddhukaushik/slimdex-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_siddhukaushik_slimdex_mcp_ffa1bfe7/readme"
}