# Vs Token Safer

> Token-safe C++/C# code search via clangd/Roslyn index instead of grep. Local-only, no IDE required. Claude Code plugin + vts CLI.

- **Type:** MCP server
- **Install:** `agentstack add mcp-jsungmin-vs-token-safer`
- **Verified:** Pending review
- **Seller:** [JSungMin](https://agentstack.voostack.com/s/jsungmin)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [JSungMin](https://github.com/JSungMin)
- **Source:** https://github.com/JSungMin/vs-token-safer

## Install

```sh
agentstack add mcp-jsungmin-vs-token-safer
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# vs-token-safer

**English** · [한국어](README.ko.md)

> A token-saving code layer for Claude Code on **any** codebase — TypeScript, JavaScript, Python, C#, C++, Go, and
> more. (Battle-tested down to a 26k-translation-unit Unreal Engine monorepo; bundles a game/build-log analyzer too.)

[](https://code.claude.com/docs/en/plugins)
[](https://modelcontextprotocol.io)
[](https://glama.ai/mcp/servers/JSungMin/vs-token-safer)
[](https://github.com/JSungMin/vs-token-safer/releases)
[](LICENSE)
[](https://github.com/JSungMin/vs-token-safer/pulls)
[](https://github.com/JSungMin/vs-token-safer/stargazers)

> Your coding agent has a small context window. Your repo is large. **vs-token-safer sits in between.**
>
> Ask where something is, what calls it, or even "how does the auth flow work?" when the name escapes you — and
> instead of pasting a wall of source into the chat, it replies with a short `file:line` list.
>
> When your project builds normally, the answers are exact: it reads the same code index your editor relies on.
> When it doesn't, it still locates your functions and classes with nothing to set up. And when you've forgotten
> the name and only remember what the code does, it finds it from the vocabulary your own code already uses — no
> AI model, nothing uploaded. Markdown and config files work the same way: jump straight to one section by its
> heading instead of opening the whole file.
>
> A companion plugin does the same for giant editor and build logs. **None of it leaves your machine.**

  

  
  The built-in dashboard (vts serve) — your indexed repo as a live 3D graph, all on 127.0.0.1.

```text
# Claude tries to grep code → the hook REWRITES it to the indexed query, in place:
$ grep -rn "createSession" src/
↻ [vs-token-safer] Rerouted → search_symbol "createSession"   # semantic, not a text match
  func createSession (in AuthService)  @ src/auth/session.ts:142   (+2 more)
  → ~120 tokens   (grep would have dumped thousands of lines)

# Editing that symbol? Name it — no Read-the-whole-file, no line counting:
$ replace_symbol_body symbol="createSession" body="…"        # preview; apply=true writes
  replace_symbol_body "createSession" — PREVIEW at src/auth/session.ts:142-160
```
Same flow on TypeScript, Python, C#, C++, Go and more (clangd · Roslyn · tsserver · pyright · tree-sitter). `VTS_REWRITE=0` blocks instead of rewriting.

## Why

- `grep` on a large repo — a TypeScript/Python monorepo, a C#/.NET solution, even a 26k-TU Unreal C++ tree — floods the context. The language-server index stays token-capped — ~97–99% smaller ([benchmarks](#performance)).
- Claude keeps reaching for `grep`. The hook doesn't just block it — it **rewrites the command to the indexed query in place**, so the search still runs and the flow never breaks.
- **Edit by symbol, not by line.** Replace/insert-around/delete a declaration by *naming* it — the index supplies the span, so you skip reading the whole file into context.
- You can't tell how much grep still slips through. `vts discover` reads your recent sessions and reports exactly which searches bypassed the index and what they cost.
- The language server runs **headlessly** — no editor open, unlike an IDE-proxy approach.

## Quickstart

```bash
# 1) Install (also auto-installs the gamedev-log-analyzer sibling)
/plugin marketplace add JSungMin/vs-token-safer
/plugin install vs-token-safer@vs-token-safer
/reload-plugins        # first run auto-installs the server deps (no manual npm)

# 2) Configure — detects the backend, asks for the project path, writes the config
/vs-token-safer:setup
```

Then **restart the Claude Code session** (the `vs-search` MCP server only starts on a fresh session).
Verify the tools appear and that `grep src/**/*.cpp` is rerouted to the index. Prerequisites: **Node ≥ 18**
and a language server — clangd (C/C++) / Roslyn (C#) you install; JS/TS + Python auto-install. Details in
[Prerequisites](#prerequisites-details) below.

> Want only the log analyzer? `/plugin install gamedev-log-analyzer@vs-token-safer`.

## How it works

  

vs-token-safer isn't a search box — it's a **precision ladder**. You ask where something is, what calls it,
or "how does the auth flow work?" when the name escapes you, and it answers at the highest precision it can
reach, then tells you which rung the answer came from:

- **EXACT** — you know the name and the project builds → the official language server (clangd / Roslyn /
  tsserver / pyright), the semantic ground truth.
- **SYNTACTIC** — no toolchain set up → a tree-sitter parse (36 languages, bundled, no native build) still
  returns real *declarations*, not a grep.
- **FUZZY** — you only remember what the code *does* → a concept dictionary mined from the repo's own
  identifiers + comments (no AI model, nothing uploaded).
- **SECTION** — it's a doc or config, not code → Markdown / TOML / YAML / CSS / HTML addressed by heading.

The rungs aren't a one-time pick — they connect, and vts **switches between them as it learns more**. Start
on FUZZY when you only know the intent; the moment `concept_search` surfaces a real name, vts climbs to
EXACT to confirm it against the semantic index (and an exact search that misses drops back down to FUZZY).
The hooks steer that hand-off in both directions, so "I don't know the name" turns into a precise,
semantically-verified `file:line` instead of a dead end.

Every answer comes back capped to `file:line` (never source bodies) and carries a one-line **completeness
certificate** naming the rung — so the model always knows whether it got the semantic truth or a fallback.
On a 3-language, 150-file benchmark that's **87% fewer tokens than grep** (~138× on a real Unreal Engine 5
tree). Underneath, four mechanisms make Claude actually *use* the ladder instead of reaching for grep:

| Layer | Effect |
| --- | --- |
| **Rewrite/enforcement hook** | Covers four surfaces. **Bash** grep/rg/`find -name` over source → **rewritten to the equivalent `vts` query in place** (identifier → `search_symbol`, literal → `search_text`, `find  -name` → `find_files` rooted at ``); ambiguous cases (pipeline, multi-`-name`) block. **Grep tool** symbol hunt (bare identifier, `::`/`(`/`void·class` regex, or a `FooBar\|BazQux` CamelCase alternation) → **blocked** with a ready-to-use call; freeform/keyword alternations stay a warn. **Glob tool** concrete code file (`*.cpp`, `Foo.h`) → **blocked** toward `find_files`. **Edit/MultiEdit** that replaces or adds a **whole declaration** → a model-visible nudge toward the symbol-edit tools (`replace_symbol_body`/`insert_symbol`), escalating to a block on a safe insert after repeated ignores (`VTS_EDIT_WARN`, `VTS_EDIT_BLOCK_AFTER`); a sub-declaration tweak stays silent. Messages are agent-directed and i18n'd (EN/KO). Logs/`.md`/config pass through. Knobs: `VTS_REWRITE=0`, `VTS_GREP_BLOCK=0`, `VTS_ENFORCE=0`. |
| **Token-capping core** | Turns LSP results into `kind name @ file:line`, caps, appends `… N more`. A refs-heavy result collapses to one row per file (`Foo.cpp:42,88,120`) with a shared dir prefix factored out once (`VTS_COMPACT_RESULTS=0` restores per-line). A truncated `find_files`/`search_text` tees the full set to a recovery file. |
| **Symbol-level editing** | `replace_symbol_body`/`insert_*`/`safe_delete` resolve a declaration by name via the outline and splice text at its exact span — preview by default, `apply=true` writes, `safe_delete` refuses while referenced. No whole-file Read into context. |
| **Headless LSP client** | A fully-owned LSP client spawns the official engine over stdio. The project root is resolved **per call** (explicit `projectPath` → the file's enclosing project → the MCP workspace root), so one global server answers for **every repo a session touches**. Live backends are pooled and bounded (`VTS_MAX_BACKENDS` + idle reaper). |
| **Savings + discover** | A local ledger records every search's tokens-saved (`vts savings`, with a 30-day graph). `vts discover` scans recent sessions for searches that *bypassed* the index — so you see the catch-rate, not just the wins. |

> **Engine = official, glue = ours.** clangd (LLVM) and Roslyn (Microsoft) do the analysis; this repo
> only writes the LSP↔MCP glue. No third-party MCP server runs over your source. Local-only, nothing uploaded.

## Tools

All search/edit goes through an official language-server index — **clangd** (C/C++), **Roslyn** (C#/.NET),
**tsserver** (JS/TS), **pyright** (Python) — and comes back as a compact, capped `file:line` list (never
source bodies). MCP server `vs-search`; same tools as the `vts` CLI.

**Search / navigate**

| Tool | CLI | Does |
| --- | --- | --- |
| `search_symbol` | `vts symbol` | Find a symbol declaration by name/substring (semantic, not text). |
| `find_references` | `vts references` | Every call site of a symbol. Takes the **name directly** (`symbol="FooBar"`) — the one to reach for when you change a function/type and must touch every use. `detail=file`/`dir` → a **blast-radius summary** (dependents grouped + ranked) instead of the per-line list. `direction=callers`/`callees` switches to a **multi-hop call hierarchy** (who *transitively* calls this = blast radius / what it calls) to `depth` hops — built on LSP `callHierarchy`, the semantic call graph, not a text scan. `vts trace-calls` = shorthand for `references --direction callers`. |
| `read_symbol` | `vts read-symbol` | Return the **source of one named declaration** (its span) — not the whole file. The read-side twin of `replace_symbol_body`: skip Read-ing a 700-line file to see one function. `signatureOnly` trims to the head. |
| `goto_definition` | `vts definition` | Jump to the definition at a position. `kind=` also does `type_definition` / `implementation` (concrete impls of an interface/virtual) / `declaration`. |
| `hover` | `vts hover` | Type/signature at a position. |
| `document_symbols` | `vts symbols` | Outline a file (classes/functions/types as `file:line`). `scope=directory` → a **signatures-only repo skeleton** of every code file under a dir (the shape of a module without Reading each file). |
| `diagnostics` | `vts diagnostics` | Compiler/linter errors + warnings as a token-capped `file:line:col severity: message` list — the compact stand-in for reading raw build output. One file by default; `scope=directory` scans the project. |
| `find_files` | `vts files` | Find files by name/glob — token-capped stand-in for `find -name`. |
| `search_text` | `vts text` | Raw text/regex search — capped stand-in for `grep` (`path=`/`glob=`/`docs=true` to target). |
| `concept_search` | `vts concept` | **Fuzzy** search for a concept you can't name (`"auth login flow"`) — mines a dictionary from the repo's own identifier+comment co-occurrence (no embeddings, nothing sent) and ranks declarations; `--flow` traces the top hit's call graph. |

**Edit (symbol-level — name it, don't line-count)** — preview by default, `apply=true` writes.

| Tool | CLI | Does |
| --- | --- | --- |
| `rename` | `vts rename` | Semantic project-wide rename (every reference, not a text sed). |
| `replace_symbol_body` | `vts replace-symbol` | Replace a whole declaration (signature + body) by name — the index supplies the span. |
| `insert_symbol` | `vts insert` | Insert text next to a declaration — `position=after` (default, e.g. a sibling method) or `before` (e.g. an import/attribute). |
| `safe_delete` | `vts safe-delete` | Delete a declaration — **refuses while it's still referenced** unless `force=true`. |

> **Docs & config too (structure tier).** Point any of `document_symbols` / `read_symbol` / `replace_symbol_body` / `insert_symbol` / `safe_delete` at a **Markdown / AsciiDoc / reST / TOML / INI / YAML / JSON / text** file and the "symbol" is a **section** (heading, `[section]`, or key): outline a 2000-line `CLAUDE.md` in ~30 lines, read or replace one `## Section` by name without Reading the whole file. No language server, no new tools — same token-safer move, for documents.

**Admin / meta — one MCP tool `vts_admin {op, params}`** (folded to keep the per-session tool-definition
cost small; the CLI keeps the bare subcommands):

| `op` | CLI | Does |
| --- | --- | --- |
| `git` / `p4` | `vts git` / `vts p4` | Run a read-only `git status/log/diff` or Perforce `opened/status/changes/reconcile`, output grouped/deduped/capped. Mutating subcommands refused. |
| `setup` / `config` | `vts setup` / `vts config` | Configure / show settings (projectPath, backend, maxResults, clangdCmd, genCompileDb). |
| `savings` / `savings_reset` | `vts savings` | Token-savings ledger (graph/daily/history) / clear it. |
| `warmup` | `vts warmup` | Pre-build the language-server index. |
| `discover` | `vts discover` | Find code searches that bypassed vts (missed savings). |
| `gen_compile_db` | `vts gen-compile-db` | Generate the Unreal clangd compile DB (UBT). |

e.g. `vts_admin {op:"git", params:{argv:["status","-s"]}}`. Or hand a whole "where is X / what calls Y /
find file W" lookup to the **`code-locator` subagent** — it searches in its own context and returns only
the `file:line` table.

**Dashboard — `vts serve`.** A local, interactive view of what vts knows + how much it saved: the
savings trend, language mix, per-tool savings, and an **interactive 3D graph** (WebGL / Three.js) with two
modes — the **include graph** (files sized by include fan-in) and an **on-demand call graph** (type a symbol
→ its transitive callers/callees, traced live through LSP `callHierarchy` — no persistent index; shows
**call counts** per node/edge). Nodes are laid out on a **spherical shell** (so they spread out, not clump);
drag/`WASD` to orbit, wheel/`+`-`-` to zoom, `R` to fit, hover for `file:line`. Symbol search has **live
autocomplete** (`/symbols`); **color by** connected-component **groups** · **repo** (which repository each
node is from, with a legend) · **heat**; **click a node to drill into its group** (`Esc`/`Backspace` to pop
out); a **focus/maximize** toggle, a highlight filter, and a node/edge metrics overlay.

Easiest via the slash commands: **`/vs-token-safer:viz`** (open) and **`/vs-token-safer:viz-stop`** (close).
Or the CLI:

```bash
vts serve --open     # → http://127.0.0.1:8731/  (launches the browser; --port N to change)
vts serve --stop     # stop it (or Ctrl-C the process)
```

It's **127.0.0.1-only and serves a fully self-contained page** — CSS/JS inlined and **Three.js vendored
locally** (`server/vendor/`, served same-origin, never a CDN), so nothing leaves the machine; it renders
with the network unplugged. Same trust model as the rest of vts. Built on Node's stdlib `http` (no
web-framework dependency), and it runs **only when you invoke it** — the MCP server never starts it, so the
steady-state package stays a thin stdio client. The 3D graph caps at `VTS_VIZ_MAX_NODES` (200) for smoothness.

```
$ vts symbol --q createSession --projectPath ./app
3 symbol(s) matching "createSession" (backend: typescript, root: ./app):
func createSession (in AuthService)  @ app/src/auth/session.ts:142
method createSessionToken (in TokenStore)  @ app/src/auth/token.ts:88
func createSessionCookie  @ app/src/http/cookies.ts:31

✓ Saved ~4,200 tokens here (96.8% / 31× smaller than the raw index response).
```

## The two plugins

| Plugin | Does | Needs |
| --- | --- | --- |
| **vs-token-safer** (this page) | Force code search/edit through the clangd/Roslyn/tsserver/pyright index over Bash grep, token-capped to `file:line` | Node + a language server (clangd / Roslyn you install; JS/TS + Python auto). No IDE. |
| **[gamedev-log-analyzer](gamedev-log-analyzer/README.md)** | Parse/dedup/classify huge Unreal/Unity/Godot/MSVC-UBT logs, search + diff + extract scalars | Node only |

`vs-token-safer` declares `gamedev-log-analyzer` as a dependency, so one install pulls in both. **Used
together:** the log analyzer emits `file:line` per entry → hand it to `goto_definition`/`find_references`
to open the code, without grepping or dumping the raw log.

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [JSungMin](https://github.com/JSungMin)
- **Source:** [JSungMin/vs-token-safer](https://github.com/JSungMin/vs-token-safer)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** yes

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-jsungmin-vs-token-safer
- Seller: https://agentstack.voostack.com/s/jsungmin
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
