Skip to main content
Glama
adborroto

semantic-search-mcp

by adborroto

semantic-search-mcp

CI Security npm node License: MIT

Eine kleine, eigenständige RAG-lite-Abrufmaschine: Sie indexiert Dateien auf der Festplatte und beantwortet die Frage "Was ist semantisch relevant für diese Abfrage?" – und sonst nichts. Sie ruft kein LLM auf und generiert keine Antworten. Sie liefert die relevantesten Textabschnitte (Datei, Zeile, Bewertung) zurück, sodass der Empfänger – ein Mensch, ein Skript oder ein LLM über MCP – selbst entscheiden kann, was damit geschehen soll.

Alles läuft lokal und offline nach dem ersten Durchlauf:

  • Embeddings: @huggingface/transformers mit Xenova/all-MiniLM-L6-v2 und int8-quantisierten Gewichten auf der CPU. Keine GPU, kein API-Schlüssel, keine Netzwerkaufrufe zur Abfragezeit.

  • Vektorspeicher: @lancedb/lancedb – eine eingebettete, dateibasierte Vektordatenbank. Kein Serverprozess, kein Docker.

  • Schnittstellen: eine CLI und ein stdio MCP-Server, sodass jeder MCP-fähige Agent (Claude Code, Cursor, Zed, …) direkt in Ihrem Korpus suchen kann.

Schnellstart

npm install -g @adborroto/semantic-search-mcp

semantic-search add ~/code/my-project      # add a folder to the corpus
semantic-search index                      # embed it (incremental on later runs)
semantic-search search "how does the retry logic work"

Das ist die gesamte Einrichtung. Es gibt keine Konfigurationsdatei, die von Hand geschrieben werden muss – add erstellt und verwaltet sie für Sie. Um es ohne Installation auszuprobieren:

npx @adborroto/semantic-search-mcp add ~/code/my-project

Hinweis zur Installationsgröße: ~950 MB Abhängigkeiten, plus ein ~25 MB großes Embedding-Modell, das bei der ersten Nutzung heruntergeladen wird. Fast alles davon sind native Binärdateien, die Sie auf dieser Ebene nicht vermeiden können – @lancedb/lancedb (~430 MB inklusive seiner plattformspezifischen Binärdatei) und die ONNX-Laufzeit (~300 MB, die Builds für jede Plattform in einem Paket enthält). Beide werden einmal zwischengespeichert; nach dem ersten Durchlauf ist alles offline.

Voraussetzungen

  • Node.js >= 22 (node:sqlite, das vom Fallback-Backend verwendet wird, ist erst ab Version 22 stabil).

  • ~950 MB Festplattenspeicher für Abhängigkeiten und ~25 MB für das Embedding-Modell, plus etwa 1–3 KB pro indexiertem Abschnitt.

  • Keine GPU, keine externen Dienste, kein Datenbankserver.

Warum "RAG-lite"

Eine vollständige RAG-Pipeline ist: Abschnitte abrufen → an ein LLM weiterleiten → LLM schreibt eine Antwort. Dieses Projekt stoppt bei Schritt eins. Das hält es einfach, schnell, kostengünstig im Betrieb und leicht nachvollziehbar – und es lässt sich sauber mit jedem LLM oder Agenten-Framework kombinieren, das Sie bereits verwenden, anstatt eine eigene, meinungsstarke Generierungsschicht zu bündeln.

Verwalten des Korpus

semantic-search add ~/code/api ~/notes     # add one or more folders
semantic-search list                       # show what's configured
semantic-search remove api                 # by folder name...
semantic-search remove ~/notes             # ...or by path
semantic-search config                     # where config + index actually live

add validiert, dass jeder Pfad ein echtes Verzeichnis ist, löst ihn in einen absoluten Pfad auf und überspringt Duplikate (einschließlich desselben Verzeichnisses, das über einen Symlink erreicht wird). remove entfernt auch die Abschnitte dieses Ordners aus dem Index, sodass sein Inhalt nicht mehr in den Ergebnissen erscheint – übergeben Sie --keep-index, wenn Sie ihn aus dem Korpus entfernen, aber durchsuchbar halten möchten.

Wo die Daten gespeichert werden

Konfiguration und Index folgen der XDG-Basisverzeichnisspezifikation, sodass sie Upgrades überstehen und von jeder Installationsmethode gemeinsam genutzt werden:

Was

Ort

Konfiguration

~/.config/semantic-search/config.json

Index + Modell-Cache

~/.local/share/semantic-search/

Überschreiben Sie alles mit SS_CONFIG_PATH, SS_INDEX_DIR, SS_MODEL_CACHE_DIR oder den Standardwerten XDG_CONFIG_HOME / XDG_DATA_HOME. SS_STORE_BACKEND=sqlite erzwingt das Fallback-Backend.

Der Index enthält den wörtlichen Text von allem, was Sie indexiert haben. Wenn Sie dies auf privaten Code richten, enthält ~/.local/share/semantic-search/ diesen Inhalt im Klartext. Übergeben Sie es niemals und hängen Sie es nicht an einen Fehlerbericht an.

Jede Option ist in src/config.js dokumentiert – Chunk-Größe, Ignorier-Muster, Modellname, top-k, Parallelität. Die direkte Bearbeitung von config.json funktioniert dafür immer noch; add/remove bewahren alle Schlüssel, die sie nicht selbst verwalten.

Verwendung

Index

semantic-search index                      # all configured folders
semantic-search index ~/code/one-project   # just this folder, ignoring config
semantic-search index --force              # reprocess everything

Die Indizierung ist inkrementell: unveränderte Dateien werden anhand des Änderungsdatums übersprungen, Dateien, deren Inhalt sich nicht geändert hat (nur berührt), überspringen die erneute Einbettung, und von der Festplatte gelöschte Dateien werden aus dem Index entfernt. Nur das, was sich tatsächlich geändert hat, wird neu verarbeitet.

Bei mehreren konfigurierten Ordnern durchläuft index diese nacheinander mit einer Kopfzeile pro Ordner und einer kombinierten Gesamtsumme:

[1/3] my-api  /home/me/code/my-api  ─────────────────────────────
  ↺ indexed   src/auth/middleware.js  (8 chunks)
  2 indexed  1,203 skipped  16 chunks  4.1s

[2/3] my-app  /home/me/code/my-app  ─────────────────────────────
  ...

──────────────────────────────────────────────────────────────
total  5 indexed  3,891 skipped  0 deleted  41 chunks  12.3s

Jeder index <path>-Aufruf entfernt nur veraltete Einträge für Dateien unter diesem Pfad, sodass die Indizierung von Ordner B niemals die Einträge von Ordner A berührt.

Nützliche Flags: --max-files <n> stoppt nach N neuen Dateien (begrenzt den Speicher bei riesigen Korpora), --concurrency <n> legt die Parallelität fest, --verbose protokolliert jede Datei auf stderr.

Suche

semantic-search search "how does the retry logic work" -k 5

Gibt eine Tabelle mit Dateipfad, Zeilennummer, Bewertung und einer Textvorschau aus. Im Hintergrund: Einbettung der Abfrage, Abruf eines Pools der nächsten Vektortreffer, Anwendung eines kleinen lexikalischen Boosts für Abschnitte, die auch die wörtlichen Abfragebegriffe enthalten, und Rückgabe der oberen k.

Ausschließen von Dateien

Die Indizierung überspringt standardmäßig node_modules/, .git/, Build-Ausgaben und Sperrdateien sowie alle Dateien über 500.000 Bytes. Um mehr auszuschließen, legen Sie eine gitignore-artige .indexignore an einem der folgenden Orte ab:

  • innerhalb eines Ordners, den Sie indizieren – Muster sind relativ zu diesem Ordner, sodass ein Repository seine eigenen generierten Ausgaben ausschließen kann;

  • neben Ihrer Konfiguration (~/.config/semantic-search/.indexignore) – gilt überall.

Siehe .indexignore.example für einen Ausgangspunkt, der Build-Artefakte von iOS, Android, Flutter, Ruby und JVM abdeckt.

MCP-Server

semantic-search mcp

Startet einen stdio-MCP-Server, der sechs Werkzeuge bereitstellt.

search(query, k?) – semantische Suche, gibt rohes JSON zurück:

[{ filePath, text, score, offset, startLine }, ...]

gather(query, k?, contextLines?) – gleiche Suche, zurückgegeben als einzelner formatierter Markdown-Block, bereit zum Einfügen in ein Kontextfenster:

### [1/5]  my-api  ·  src/auth/session.js  ·  line 42  ·  score 0.923
```
...chunk text...
```

contextLines (Standard 0) liest N zusätzliche Zeilen um jeden Abschnitt aus der Quelldatei – nützlich, wenn eine Abschnittsgrenze benötigten Kontext abschneidet.

list_folders() – jeder konfigurierte Ordner mit seinem Namen und absoluten Pfad. Ein guter erster Aufruf, damit der Agent weiß, welcher Korpus existiert.

cat_file(filePath, startLine?, endLine?) – liest eine Datei anhand des absoluten Pfads, wie von search/gather zurückgegeben. Beschränkt auf die konfigurierten Ordner (siehe Sicherheit).

grep(pattern, folder?, fileGlob?, caseSensitive?, maxResults?) – wörtliche oder Regex-Suche im Korpus, für den Fall, dass Sie exakte Übereinstimmungen anstelle von Ähnlichkeit benötigen. Beachtet dieselben .indexignore-Regeln wie die Indizierung.

my-api  ·  src/auth/session.js:42  export function createSession(user) {

index(root?, force?, maxFiles?, concurrency?) – löst eine inkrementelle Neuindizierung aus, sodass ein Agent den Korpus aktualisieren kann, ohne ein externes Programm aufzurufen.

Alle Suchwerkzeuge teilen sich denselben Bewertungs- und Dateiauflösungscode wie die CLI; keines implementiert ihn neu.

Registrieren bei einem MCP-Client

Claude Code:

claude mcp add --scope user semantic-search -- semantic-search mcp
claude mcp list   # should show "✔ Connected"

Jeder Client, der eine JSON-Serverdefinition akzeptiert:

{
  "mcpServers": {
    "semantic-search": {
      "command": "semantic-search",
      "args": ["mcp"]
    }
  }
}

Bevorzugen Sie hier eine globale Installation gegenüber npx: Ein bloßes npx löst das Paket bei jedem Start des Servers neu auf, was die Startlatenz erhöht und Upgrades unangekündigt übernimmt. Wenn Sie npx verwenden, legen Sie die Version fest – npx -y @adborroto/semantic-search-mcp@0.1.0 mcp.

Neue MCP-Server werden normalerweise nur beim Start einer Sitzung übernommen, also starten Sie nach der Registrierung eine neue Sitzung.

Sicherheit

Dies ist ein lokales Einzelbenutzer-Werkzeug mit einem einfachen Vertrauensmodell: Alles innerhalb eines konfigurierten Ordners ist für jeden MCP-Client lesbar, der den Server erreichen kann.

  • cat_file verweigert Pfade außerhalb der konfigurierten Ordner, löst zuerst Symlinks auf, sodass ein innerhalb eines Ordners platzierter Link nicht zur Flucht verwendet werden kann.

  • grep wendet Ihre .indexignore-Regeln an, sodass Dateien, die bewusst von der Indizierung ausgeschlossen wurden, nicht durch eine exakte Übereinstimmungssuche durchsickern.

  • Unterprozesse werden mit argv-Arrays (niemals einer Shell) erzeugt, sodass Muster keine Befehle einschleusen können.

Vor diesem Hintergrund: Richten Sie es nicht auf einen Korpus, den Sie nicht Ihrem LLM-Anbieter geben würden – Abschnitte werden an den Client zurückgegeben, der sie angefordert hat. Siehe SECURITY.md.

Wie es funktioniert

Aufteilung in Abschnitte

Text wird in Absätze aufgeteilt und dann gierig in Abschnitte von etwa 200 Token mit ~35 Token Überlappung gepackt, gezählt mit dem echten Tokenizer des Embedding-Modells und nicht mit einer Zeichenzählungsnäherung. Dies ist nicht willkürlich: all-MiniLM-L6-v2 hat ein Fenster von 256 Token und kürzt alles Längere stillschweigend ab, daher sind die Abschnitte so dimensioniert, dass sie mit Spielraum für die [CLS]/[SEP]-Token hineinpassen. Die Überlappung wird zusätzlich begrenzt, sodass die Überlappung plus der nächste Absatz diese Grenze niemals überschreiten kann – andernfalls würde das Ende eines Abschnitts beim Einbetten verworfen, während es von search dennoch zurückgegeben wird.

Ein einzelner Absatz, der größer als die harte Grenze ist (ein minifiziertes Bundle, eine riesige Protokollzeile), fällt auf die Wort-für-Wort-Packung mit derselben Überlappungslogik zurück, und jedes einzelne "Wort" über 500 Zeichen wird zuerst aufgeteilt, sodass dem Tokenizer nie etwas Riesiges auf einmal übergeben wird.

Token-Anzahlen werden einmal pro Absatz/Wort berechnet und zwischengespeichert für die Wiederverwendung während der Überlappungsberechnung. Eine frühere Version tokenisierte bei jeder Überlappungssuche neu, was bei kleinen Eingaben in Ordnung war, aber bei großen Repositorys zu ausufernder CPU-Auslastung und mehrfachem GB-Speicherwachstum führte. Wenn Sie den Chunker erweitern, bewahren Sie diese Eigenschaft.

Inkrementelle Neuindizierung

Es gibt kein separates Manifest – der Vektorspeicher ist das Manifest. Jeder gespeicherte Abschnitt trägt die mtimeMs seiner Quelldatei und einen sha256-Inhalts-Hash. Bei jedem Durchlauf:

  1. Wenn die mtime einer Datei auf der Festplatte mit der gespeicherten übereinstimmt, überspringe sie, ohne die Datei zu lesen.

  2. Wenn sich die mtime geändert hat, aber der Inhalts-Hash identisch ist (ein touch), überspringe die erneute Einbettung.

  3. Andernfalls lösche die alten Abschnitte dieser Datei und füge neu eingebettete ein.

  4. Nach dem Durchlauf werden alle indizierten Pfade, die nicht mehr auf der Festplatte (und unter dem zu indizierenden Stammverzeichnis) vorhanden sind, entfernt.

Speicher-Backends

Der Standard ist LanceDB: eingebettet, dateibasiert, echte Vektorsuche. Ein node:sqlite + Brute-Force-Cosinus-Fallback (src/store/sqliteFallbackStore.js) implementiert dasselbe Interface (src/store/vectorStore.js) für Umgebungen, in denen die native Bindung von LanceDB nicht geladen wird – sandboxierte Container, ungewöhnliche Architekturen. Wechsel mit SS_STORE_BACKEND=sqlite.

Das Fallback führt einen vollständigen Tabellenscan pro Suche durch: in Ordnung für Zehntausende von Abschnitten, nicht darüber hinaus. Die Standardmetrik von LanceDB ist L2, nicht Cosinus, daher setzt dieses Projekt explizit .distanceType('cosine') für jede Abfrage, da Embeddings als normalisierte Vektoren verglichen werden.

Projektstruktur

src/
  config.js            Defaults + config file resolution (XDG) — the only source of tunables
  configFile.js        Read/modify/write the config file (backs add/remove/list)
  embeddings.js        transformers.js pipeline + tokenizer (lazy singletons)
  chunker.js           Token-aware paragraph packing with overlap
  ignoreRules.js       .indexignore layering, shared by the indexer and grep
  safePath.js          Path confinement for the MCP file-reading tools
  version.js           Version read from package.json
  extractors/          text (.txt .md .js .ts .py .rb .json), pdf (pdf-parse), docx (mammoth)
  store/
    vectorStore.js        Storage interface + backend selector
    lancedbStore.js       LanceDB implementation (default)
    sqliteFallbackStore.js node:sqlite + manual cosine fallback
  indexer.js           Walk + extract + chunk + embed + incremental upsert/prune
  search.js            Embed query + vector search + lexical boost — shared by CLI and MCP
  mcp-server.js        MCP stdio server: the six tools above
  index.js             CLI entrypoint (commander)
scripts/index-all.sh   Batched indexing for very large corpora on constrained hosts (Linux)

Entwicklung

git clone https://github.com/adborroto/semantic-search-mcp.git
cd semantic-search-mcp
npm install
npm test              # unit + end-to-end (node:test, no framework)
npm run test:unit     # skip the slow end-to-end test
npm run lint

Eine config.json im Checkout-Stammverzeichnis hat Vorrang vor dem XDG-Speicherort, sodass Sie mit einem Testkorpus entwickeln können, ohne Ihre eigentliche Einrichtung zu beeinträchtigen. Tests schreiben immer in temporäre Verzeichnisse. Siehe CONTRIBUTING.md.

Nicht im Lieferumfang enthalten (designbedingt)

  • Antwortgenerierung. Dies gibt Abschnitte zurück, keine Antworten. Füttern Sie sie selbst an ein LLM.

  • Neubewertung mit einem zweiten Modell. Der lexikalische Boost ist eine billige, abhängigkeitsfreie Näherung – kein Ersatz für einen echten Cross-Encoder-Neubewerter.

  • Eine Weboberfläche. Nur CLI und MCP.

  • Massive Korpora. Entwickelt für einen persönlichen oder teamgroßen Korpus aus Dokumenten und Code – Zehntausende von Abschnitten, nicht Millionen. Beide Backends gehen von diesem Maßstab aus.

Lizenz

MIT

-
license - not tested
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.

  • Agentic search over your Dewey document collections from any MCP-compatible client.

  • Hosted MCP memory: save sessions/decisions once, search from Claude, Cursor, ChatGPT. EU-hosted FTS.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/adborroto/semantic-search-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server