semantic-search-mcp
semantic-search-mcp
Миниатюрный, самодостаточный движок извлечения в стиле RAG-lite: индексирует файлы на диске и отвечает на вопрос «что семантически релевантно этому запросу» — и ничего больше. Он не вызывает LLM и не генерирует ответы. Он возвращает наиболее релевантные текстовые фрагменты (файл, строка, оценка), чтобы тот, кто их потребляет — человек, скрипт или LLM через MCP — сам решал, что с ними делать.
Всё работает локально и офлайн после первого запуска:
Эмбеддинги:
@huggingface/transformersс модельюXenova/all-MiniLM-L6-v2в int8-квантованных весах на CPU. Ни GPU, ни API-ключа, ни сетевых вызовов во время запроса.Векторное хранилище:
@lancedb/lancedb— встраиваемая, файловая векторная база данных. Ни серверного процесса, ни Docker.Интерфейсы: CLI и stdio MCP-сервер, так что любой MCP-совместимый агент (Claude Code, Cursor, Zed, …) может напрямую искать в вашем корпусе.
Быстрый старт
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"Вот и вся настройка. Никаких конфигурационных файлов, которые нужно писать вручную — add создаёт и управляет им за вас. Чтобы попробовать без установки чего-либо:
npx @adborroto/semantic-search-mcp add ~/code/my-projectВажно о размере установки: ~950 МБ зависимостей, плюс модель эмбеддингов (~25 МБ), загружаемая при первом использовании. Почти всё это — нативные бинарные файлы, которых на этом уровне не избежать —
@lancedb/lancedb(~430 МБ, включая платформенный бинарник) и среда выполнения ONNX (~300 МБ, которая поставляется со сборками для всех платформ в одном пакете). Оба кэшируются однократно; всё после первого запуска работает офлайн.
Требования
Node.js >= 22 (
node:sqlite, используемый запасным бэкендом, стабилен только с версии 22).~950 МБ на диске для зависимостей и ~25 МБ для модели эмбеддингов, плюс примерно 1–3 КБ на каждый проиндексированный фрагмент.
Ни GPU, ни внешних сервисов, ни сервера баз данных.
Почему «RAG-lite»
Полный пайплайн RAG выглядит так: извлечение фрагментов → передача их LLM → LLM пишет ответ. Этот проект останавливается на первом шаге. Это делает его простым, быстрым, дешёвым в работе и лёгким для понимания — и он чисто компонуется с любым LLM или фреймворком агентов, который вы уже используете, вместо того чтобы включать собственный, жёстко заданный слой генерации.
Управление корпусом
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 liveadd проверяет, что каждый путь является реальной директорией, преобразует его в абсолютный путь и пропускает дубликаты (включая одну и ту же директорию, достигнутую через символическую ссылку). remove также удаляет фрагменты этой папки из индекса, так что её содержимое перестаёт появляться в результатах — передайте --keep-index, если хотите убрать её из корпуса, но оставить доступной для поиска.
Где хранятся данные
Конфигурация и индекс следуют спецификации базовых директорий XDG, поэтому они переживают обновления и являются общими для всех способов установки:
Что | Расположение |
Конфигурация |
|
Индекс + кэш модели |
|
Любой из этих путей можно переопределить с помощью SS_CONFIG_PATH, SS_INDEX_DIR, SS_MODEL_CACHE_DIR или стандартных XDG_CONFIG_HOME / XDG_DATA_HOME. SS_STORE_BACKEND=sqlite принудительно включает запасной бэкенд.
Индекс содержит дословный текст всего, что вы проиндексировали. Если вы укажете его на приватный код,
~/.local/share/semantic-search/будет хранить этот контент в открытом тексте. Никогда не помещайте его в систему контроля версий и не прикрепляйте к отчёту об ошибке.
Каждая опция документирована в src/config.js — размер фрагментов, шаблоны игнорирования, имя модели, top-k, конкурентность. Редактирование config.json напрямую по-прежнему работает для них; add/remove сохраняют любые ключи, которые им не принадлежат.
Использование
Индексация
semantic-search index # all configured folders
semantic-search index ~/code/one-project # just this folder, ignoring config
semantic-search index --force # reprocess everythingИндексация является инкрементальной: неизменённые файлы пропускаются по времени модификации, файлы, чьё содержимое на самом деле не изменилось (только время доступа), пропускают повторное эмбеддинг, а файлы, удалённые с диска, удаляются из индекса. Обрабатывается только то, что действительно изменилось.
Если настроено несколько папок, index обходит их последовательно с заголовком для каждой папки и общим итогом:
[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Каждый вызов index <path> удаляет устаревшие записи только для файлов внутри этого пути, поэтому индексация папки B никогда не затрагивает записи папки A.
Полезные флаги: --max-files <n> останавливается после N новых файлов (ограничивает память для огромных корпусов), --concurrency <n> задаёт параллелизм, --verbose выводит каждый файл в stderr.
Поиск
semantic-search search "how does the retry logic work" -k 5Выводит таблицу с путём к файлу, номером строки, оценкой и текстовым превью. Под капотом: эмбеддинг запроса, извлечение пула ближайших векторных совпадений, небольшое лексическое повышение для фрагментов, которые также содержат буквальные термины запроса, и возврат верхних k результатов.
Исключение файлов
Индексация по умолчанию пропускает node_modules/, .git/, результаты сборки и файлы блокировок, а также любые файлы размером более 500 000 байт. Чтобы исключить больше, поместите файл .indexignore в стиле gitignore в одном из следующих мест:
внутри индексируемой папки — шаблоны относительны к этой папке, поэтому репозиторий может исключать свои собственные сгенерированные выходные данные;
рядом с вашей конфигурацией (
~/.config/semantic-search/.indexignore) — применяется везде.
Смотрите .indexignore.example для отправной точки, охватывающей артефакты сборки iOS, Android, Flutter, Ruby и JVM.
MCP-сервер
semantic-search mcpЗапускает stdio MCP-сервер, предоставляющий шесть инструментов.
search(query, k?) — семантический поиск, возвращает сырой JSON:
[{ filePath, text, score, offset, startLine }, ...]gather(query, k?, contextLines?) — тот же поиск, возвращаемый в виде одного отформатированного Markdown-блока, готового к вставке в контекстное окно:
### [1/5] my-api · src/auth/session.js · line 42 · score 0.923
```
...chunk text...
```contextLines (по умолчанию 0) читает N дополнительных строк вокруг каждого фрагмента из исходного файла — полезно, когда граница фрагмента обрезает необходимый контекст.
list_folders() — каждая настроенная папка с её именем и абсолютным путём. Хороший первый вызов, чтобы агент знал, какой корпус существует.
cat_file(filePath, startLine?, endLine?) — чтение файла по абсолютному пути, как возвращается search/gather. Ограничено настроенными папками (см. Безопасность).
grep(pattern, folder?, fileGlob?, caseSensitive?, maxResults?) — буквальный или регекс-поиск по корпусу для случаев, когда нужны точные совпадения, а не сходство. Соблюдает те же правила .indexignore, что и индексация.
my-api · src/auth/session.js:42 export function createSession(user) {index(root?, force?, maxFiles?, concurrency?) — запуск инкрементальной переиндексации, чтобы агент мог обновить корпус без вызова внешней команды.
Все инструменты поиска используют тот же код ранжирования и разрешения файлов, что и CLI; ни один из них не перереализует его.
Регистрация в MCP-клиенте
Claude Code:
claude mcp add --scope user semantic-search -- semantic-search mcp
claude mcp list # should show "✔ Connected"Любой клиент, принимающий JSON-определение сервера:
{
"mcpServers": {
"semantic-search": {
"command": "semantic-search",
"args": ["mcp"]
}
}
}Здесь предпочтительнее глобальная установка, а не npx: простой npx заново разрешает пакет при каждом запуске сервера, добавляя задержку при запуске и незаметно подхватывая обновления. Если вы используете npx, зафиксируйте версию — npx -y @adborroto/semantic-search-mcp@0.1.0 mcp.
Новые MCP-серверы обычно подхватываются только при запуске сессии, поэтому после регистрации начните новую сессию.
Безопасность
Это локальный, однопользовательский инструмент с простой моделью доверия: всё, что находится внутри настроенной папки, доступно для чтения любому MCP-клиенту, который может подключиться к серверу.
cat_fileотказывает в доступе к путям за пределами настроенных папок, сначала разрешая символические ссылки, чтобы ссылка, помещённая внутрь папки, не могла быть использована для выхода за её пределы.grepприменяет ваши правила.indexignore, поэтому файлы, намеренно исключённые из индексации, не просачиваются через поиск точных совпадений.Дочерние процессы запускаются с массивами argv (никогда не через оболочку), поэтому шаблоны не могут внедрять команды.
Учитывая это, не указывайте его на корпус, который вы не передали бы своему LLM-провайдеру — фрагменты возвращаются тому клиенту, который их запросил. Смотрите SECURITY.md.
Как это работает
Разбиение на фрагменты
Текст разбивается на абзацы, затем жадно упаковывается в фрагменты примерно по 200 токенов с ~35 токенами перекрытия, подсчитанными с помощью реального токенизатора модели эмбеддингов, а не приближения по количеству символов. Это не произвольно: all-MiniLM-L6-v2 имеет окно в 256 токенов и молча обрезает всё, что длиннее, поэтому фрагменты имеют размер, чтобы поместиться в него с запасом для токенов [CLS]/[SEP]. Перекрытие дополнительно ограничено так, чтобы перекрытие плюс следующий абзац никогда не могли превысить этот лимит — иначе хвост фрагмента был бы отброшен во время эмбеддинга, но всё равно возвращался бы search.
Один абзац, превышающий жёсткий лимит (минифицированный бандл, одна гигантская строка лога), возвращается к упаковке на уровне слов с той же логикой перекрытия, и любое отдельное «слово» длиннее 500 символов сначала разрезается, так что токенизатору никогда не передаётся ничего огромного за один раз.
Подсчёт токенов выполняется один раз на абзац/слово и кэшируется для повторного использования при расчёте перекрытия. Более ранняя версия повторно токенизировала при каждом поиске перекрытия, что было нормально для небольших входных данных, но вызывало неконтролируемый рост загрузки CPU и многогигабайтный рост памяти на больших репозиториях. Если вы расширяете модуль разбиения, сохраните это свойство.
Инкрементальная переиндексация
Нет отдельного манифеста — векторное хранилище является манифестом. Каждый сохранённый фрагмент несёт mtimeMs исходного файла и хеш содержимого sha256. При каждом запуске:
Если
mtimeфайла на диске совпадает с сохранённым, пропустить его без чтения файла.Если
mtimeизменилось, но хеш содержимого идентичен (командаtouch), пропустить повторный эмбеддинг.В противном случае удалить старые фрагменты этого файла и вставить заново эмбеддированные.
После обхода любые индексированные пути, которых больше нет на диске (и которые находятся в корне индексируемой папки), удаляются.
Бэкенды хранения
По умолчанию используется LanceDB: встраиваемое, файловое, настоящее векторное хранилище. Запасной вариант на node:sqlite + полный перебор с косинусным сходством (src/store/sqliteFallbackStore.js) реализует тот же интерфейс (src/store/vectorStore.js) для сред, где нативная привязка LanceDB не загружается — изолированные контейнеры, необычные архитектуры. Переключение с помощью SS_STORE_BACKEND=sqlite.
Запасной вариант выполняет полное сканирование таблицы при каждом поиске: нормально для десятков тысяч фрагментов, не более. Метрика LanceDB по умолчанию — L2, а не косинусная, поэтому этот проект явно устанавливает .distanceType('cosine') для каждого запроса, так как эмбеддинги сравниваются как нормализованные векторы.
Структура проекта
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)Разработка
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 lintconfig.json в корне рабочей копии имеет приоритет над расположением XDG, поэтому вы можете разрабатывать, используя тестовый корпус, не затрагивая вашу реальную настройку. Тесты всегда пишут во временные директории. Смотрите CONTRIBUTING.md.
Вне области видимости (преднамеренно)
Генерация ответов. Этот инструмент возвращает фрагменты, а не ответы. Передайте их в LLM самостоятельно.
Переранжирование с помощью второй модели. Лексическое повышение — это дешёвая, не требующая зависимостей аппроксимация, а не замена настоящего кросс-энкодерного переранжировщика.
Веб-интерфейс. Только CLI и MCP.
Корпуса огромного масштаба. Создан для корпуса документов и кода размером с личный или командный — десятки тысяч фрагментов, а не миллионы. Оба бэкенда рассчитаны на такой масштаб.
Лицензия
This server cannot be installed
Maintenance
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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