Self-hosted service
shinpr/mcp-local-rag avatar
shinpr/mcp-local-rag

mcp-local-rag: a local hybrid-search index for MCP clients and the terminal

Local-first RAG server for developers. Semantic + keyword search for code and technical docs. Works with MCP or CLI. Fully private, zero setup.

395 stars72 forksTypeScriptMIT

At a glance

What is it?
A TypeScript MCP server that indexes PDF, DOCX, Markdown and text files on your own machine, then answers queries with semantic similarity plus keyword boosting. It suits teams whose documents cannot leave the laptop, and it stops short of source code and spreadsheets.
Who is it for?
Adopt mcp-local-rag if your working set is PDF, DOCX, Markdown or plain text, it must stay on the machine, and you already run an MCP client or are willing to drive the CLI. Skip it if you need source-code ingestion, Excel or PowerPoint, or if you want a persistent record of sync jobs across server restarts, since the README states only one job is retained per process.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The confidentiality constraint that mcp-local-rag is built around

Hosted embedding APIs require shipping document text to a third party. The README states the motivation directly: some document sets cannot be sent to a hosted embedding service because of confidentiality or organizational policy, and keeping the index local makes them searchable without adding a per-query API cost. That is the whole pitch. The audience is developers who already have a corpus of technical documents (API specs, vendor PDFs, internal Markdown) and an MCP-capable coding tool, and who cannot or will not route that corpus through an external service. The second problem is retrieval quality. Semantic search alone can miss exact identifiers, and technical documentation is full of them: API names, class names, error codes. mcp-local-rag pairs semantic retrieval with keyword matching so a query for an exact string still surfaces. If your documents are public and you are happy with a hosted index, this project solves a problem you do not have.

What runs where: parsing, embeddings, storage, search

Document parsing, embeddings, storage, and search all execute on your machine, according to the README. After the first model download, ingestion and search work offline. The server speaks the standard MCP protocol over a local stdio transport, which is why the same index is reachable from Claude Code, Codex, OpenCode, Cursor, or the CLI. Ingestion is not naive character splitting: documents are split at topic boundaries, and Markdown code blocks are kept intact. That matters for technical docs where a fenced example is meaningless if the splitter cuts it in half. Chunking is only half the story. The README documents a read_chunk_neighbors tool that returns surrounding chunks from a search result, which is the escape hatch when a retrieved fragment starts mid-explanation. HTML is handled differently from files: fetching is not built into the server, so an MCP client fetches a page and passes the HTML to ingest_data, where it is cleaned with Readability and converted to Markdown. The index is reconciled rather than rebuilt: sync_start ingests new and changed files, skips byte-identical ones, and removes entries for files that no longer exist.

Getting it running: BASE_DIR, DB_PATH, and the sync step

Node.js 22 or later is required, plus internet access on first use for the npm package and the embedding model. The default model is roughly 90 MB and the README warns the first sync may take 1 to 2 minutes before ingestion starts; later runs use the local cache. Registration is one command per client. For Claude Code: claude mcp add local-rag --scope user --env BASE_DIR=/absolute/path/to/your/documents -- npx -y mcp-local-rag. Codex takes a TOML block in ~/.codex/config.toml under [mcp_servers.local-rag] with command = "npx", args = ["-y", "mcp-local-rag"] and a [mcp_servers.local-rag.env] section holding BASE_DIR. Cursor uses ~/.cursor/mcp.json with an mcpServers object, and OpenCode uses ~/.config/opencode/opencode.json with a "type": "local" entry. After restarting the client, you ask it to sync and wait. The CLI path skips MCP entirely: npx mcp-local-rag ingest ./docs/ followed by npx mcp-local-rag query "authentication API". The CLI defaults its document root to the current directory, so both commands must run from the same place or you must set BASE_DIR and DB_PATH explicitly. Two environment keys recur: BASE_DIR, which is also the security boundary for file operations, and STORE_IMAGES, which must be set to true in the MCP server environment before sync to store supported PDF and DOCX images for new or changed files.

Where the index stops: file types, images, and the sync job record

The supported input table is explicit. PDF, DOCX, TXT and Markdown come in through file ingestion or directory sync. HTML arrives via ingest_data after the client fetches it. Plain text or Markdown held in memory goes through ingest_data with a stable source identifier. Excel, PowerPoint, standalone images, and source-code file extensions are not supported by file ingestion. That last exclusion is the one most likely to bite a developer, because the tool is marketed at developers and the obvious use case is searching a codebase. It does not do that. PDFs can optionally use a local vision model to describe figures, but the README is careful to note this is not OCR and not image search, so a scanned PDF without a text layer is not rescued by it. There is also a state-management limit worth reading twice: only one sync job is retained by the server process, a newer job replaces a finished record, and restarting the server discards it. A long sync interrupted by a restart leaves you polling a jobId that no longer exists. One more constraint: a changed PDF keeps the visual profile it was indexed with, and sync_start cannot change it.

How this differs from a general-purpose vector store

The natural alternative is a general vector database such as Qdrant or Chroma paired with a local embedding model and a custom ingestion script. The difference is not the storage engine. It is that mcp-local-rag arrives as a working pipeline with the chunking, the hybrid scoring, the file-type parsers and the MCP tool surface already wired together, and with BASE_DIR acting as a hard boundary on what the server may read. A vector store gives you a client library and leaves parsing, chunk boundaries, re-sync logic and the MCP tool definitions to you. That is more work but also more control: you can add a source-code parser, swap in a domain-specific embedding model, or keep sync history in a database rather than in process memory. The trade is real in both directions. If your corpus is exactly the four supported formats and you want it searchable this afternoon, the assembled pipeline wins. If your corpus is source code or spreadsheets, no amount of configuration here will help, and you are better off building on the vector store directly.

Licence and the ongoing cost of keeping the index current

The project is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are preserved. That is a permissive arrangement and it is the same licence as most of the MCP tooling ecosystem, so there is no compatibility puzzle to solve. This is a description of the licence text, not legal advice; if your organization has policies about bundled dependencies, check them against the actual LICENSE file in the repository. On maintenance: the release cadence visible in the material is rapid, with v0.18.4, v0.18.3 and v0.18.2 all landing within a few days of each other in early September 2026. Frequent patch releases during active development usually mean the MCP tool surface or configuration keys can shift between versions, and since clients pin the package through npx -y mcp-local-rag, you are tracking the latest published version by default rather than a version you chose. Pinning explicitly in your client config is the straightforward mitigation. The other recurring cost is re-sync: changed files are re-embedded, which is CPU time on your machine, and the README notes unchanged files are skipped by byte comparison, so the cost scales with churn rather than corpus size.

Who should run this, and what to check before you commit

The fit is narrow and clear. You have a directory of PDFs, DOCX files, Markdown or text, you use an MCP-capable editor or are comfortable with a terminal, and the documents cannot go to a hosted embedding service. In that situation the setup is one client config entry plus a sync prompt, and the payoff is offline search over material you otherwise could not query at all. The misfit is equally clear: source code, Excel, PowerPoint and standalone images are outside the ingestion path, and no configuration key changes that. If you need a durable audit trail of ingestion runs, the single in-process job record is a design choice you would have to work around. Two things are worth verifying on your own corpus before standardizing on it. First, run a handful of real queries through npx mcp-local-rag query and judge whether the default embedding model handles your language and terminology, since the README presents model choice as configurable but does not recommend one for any particular domain. Second, decide the exact value of BASE_DIR before you register the server anywhere, because it is both the document root and the boundary that MCP file paths must stay inside.

Editorial conclusion

Adopt mcp-local-rag if your working set is PDF, DOCX, Markdown or plain text, it must stay on the machine, and you already run an MCP client or are willing to drive the CLI. Skip it if you need source-code ingestion, Excel or PowerPoint, or if you want a persistent record of sync jobs across server restarts, since the README states only one job is retained per process. Before rolling it out, verify two things yourself: that the default embedding model handles your language and domain well enough on a sample query, and that BASE_DIR points at the narrowest directory you can live with, because it doubles as the file-operation boundary.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. shinpr/mcp-local-rag on GitHub
Community notes

Community notes