Token Optimizer MCP
Local stdio server that caches, diffs and compresses what a coding agent reads to cut context use
Local semantic and keyword search over your own PDF, DOCX, Markdown and text files
By shinpr (individual) TypeScript Local · stdio Knowledge and memoryFiles and storage
67 popularity score ★ 428 ↓ 4.1k/wk
Opens the app, which asks before it adds the server. Then replace each <NAME> in its settings with your value.
It needs BASE_DIR. Replace each <NAME> with your value.
claude mcp add local-rag -e BASE_DIR=<BASE_DIR> -- npx -y mcp-local-rag{
"mcpServers": {
"local-rag": {
"command": "npx",
"args": [
"-y",
"mcp-local-rag"
],
"env": {
"BASE_DIR": "<BASE_DIR>"
}
}
}
}{
"mcpServers": {
"local-rag": {
"command": "npx",
"args": [
"-y",
"mcp-local-rag"
],
"env": {
"BASE_DIR": "<BASE_DIR>"
}
}
}
}{
"servers": {
"local-rag": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"mcp-local-rag"
],
"env": {
"BASE_DIR": "<BASE_DIR>"
}
}
}
}[mcp_servers.local-rag]
command = "npx"
args = ["-y", "mcp-local-rag"]
[mcp_servers.local-rag.env]
BASE_DIR = "<BASE_DIR>"gemini mcp add --scope user -e BASE_DIR=<BASE_DIR> local-rag npx -- -y mcp-local-rag| Set together: document roots | |
|---|---|
BASE_DIR |
Required: Absolute path of the document folder and the boundary for file access. |
BASE_DIRS |
Optional: JSON array of several document folders; takes precedence over BASE_DIR. |
IGNORE_PATHS |
Optional: JSON array of glob patterns, relative to each root, for files to keep out of the index; sync also drops matches already indexed. |
DB_PATH |
Optional: Where the LanceDB index is stored; set an absolute path. |
CACHE_DIR |
Optional: Where downloaded models are cached; set an absolute path. |
MODEL_NAME |
Optional: Hugging Face embedding model; changing it needs a new DB_PATH. |
MAX_FILE_SIZE |
Optional: Largest file to ingest in bytes, at most 524288000. |
RAG_HYBRID_WEIGHT |
Optional: Keyword boost from 0.0 to 1.0. |
RAG_MAX_FILES |
Optional: Keep results from only the top N files. |
RAG_RERANK_CMD |
Optional: External command that reorders results; it receives matched text on stdin. |
STORE_IMAGES |
Optional: Set true to store PDF figures and DOCX images with matching text. |
RAG_DEVICE |
Optional: ONNX Runtime device for embeddings. |
MCP Local RAG builds a search index of documents on your own machine and lets an MCP client query it. It splits PDF, DOCX, Markdown and plain-text files into chunks, embeds them with a Hugging Face model run locally through Transformers.js, and stores the vectors in an embedded LanceDB database on disk. Queries mix vector similarity with a keyword boost, so exact terms such as error codes or class names still rank well, and each hit comes back with its source passage and, where it can tell, the heading, line or page.
The server exposes nine tools: a background sync that reconciles the index with the document folders, single-file and in-memory ingestion (text, Markdown, or HTML the client has already fetched), search, reading the chunks around a hit, listing files and their indexed state, deletion, and a status report. The same package also works as a command-line tool for ingesting and querying without an MCP client, with JSON output.
No API key or external service is needed. After the embedding model (about 90 MB by default) is downloaded once, ingestion and search run offline. Optional extras include local captioning of PDF figures with a vision model (about 250 MB, or 1.7 GB for the quality profile), storing images alongside text, passing results through an external rerank command you supply, and IGNORE_PATHS, a JSON list of glob patterns for files to keep out of the index.
What the code at v0.23.1 adds to the README: query_documents returns 10 results by default and takes a limit from 1 to 100. read_chunk_neighbors fetches 2 chunks either side by default, up to 50, and folder scans stop 10 levels deep (src/utils/limits.ts). The server also refuses to use /etc, /usr, /sys, /proc, /var, ~/.ssh or ~/.gnupg as a document root, and every tool that needs a root then fails with that error (src/utils/sensitive-path.ts, src/server-main.ts).
| Runtime | Node.js 22.20+ |
|---|---|
| Runs on | macOS, Windows, Linux |
| Also |
|
The README asks for Node.js 22.20 or later, but package.json at v0.23.1 declares ^22.23.4, ^24.22.1 or >=26.12.1, so use a current 22, 24 or 26 release. Internet access is needed on first run to fetch the npm package and the embedding model from Hugging Face (HF_ENDPOINT can point at a mirror). PDF figure captioning downloads a further 250 MB or 1.7 GB model when first used. Give the document folder by absolute path in BASE_DIR or BASE_DIRS. Runs on CPU by default.
File ingestion handles only PDF, DOCX, TXT and Markdown; Excel, PowerPoint, standalone images and source-code files are not supported, and the server does not fetch web pages itself. Figure captions are generated descriptions, not OCR. PDF section headings may be wrong. Only one sync job is kept and it is lost when the server exits. Changing the embedding model needs a fresh index at a new DB_PATH; changing RAG_DTYPE or the prefix options needs a full re-ingest. Only one writer may use a DB_PATH at a time. Unset DB_PATH and CACHE_DIR resolve from the working directory, so each project can get its own index. RAG_RERANK_CMD runs without a shell, so a Windows .cmd shim will not start.
Suits a single developer or researcher who wants an assistant to search a private folder of manuals, specs, papers or notes without sending text to an embedding API, and who can run a recent Node 22 or later locally. Hybrid scoring helps with technical material full of exact identifiers. It is not built for shared or multi-user use: it has no authentication and assumes one writer per index. Pick something else to search spreadsheets, slide decks or code files, to crawl the web, or to index scanned PDFs that need OCR.
5 only read, 4 change things. A ? marks an optional parameter.
sync_start changespath?: stringsync_statusjobId: stringingest_file changesfilePath: stringvisual?: booleanvisualQuality?: enumingest_data changescontent: stringmetadata: objectquery_documentsquery: stringlimit?: numberscope?: anyread_chunk_neighborsfilePath?: stringsource?: stringchunkIndex: numberbefore?: numberafter?: numberlist_filesscope?: anydelete_file changesfilePath?: stringsource?: stringstatus