Skip to main content
Glama
adborroto

semantic-search-mcp

by adborroto

semantic-search-mcp

CI Security npm node License: MIT

一个小巧、自包含的轻量级RAG检索引擎:它对磁盘上的文件建立索引,并回答“什么内容与这个查询语义相关”——仅此而已。它调用LLM,也生成答案。它返回最相关的文本块(文件、行号、分数),让消费方——无论是人、脚本,还是通过MCP的LLM——自行决定如何处理它们。

首次运行后,一切都在本地离线运行:

  • 嵌入模型@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

注意安装大小:约950MB的依赖项,加上首次使用时下载的约25MB嵌入模型。其中几乎全部是原生二进制文件,在这一层无法避免——@lancedb/lancedb(约430MB,包含其平台二进制文件)和ONNX运行时(约300MB,在一个包中为所有平台提供构建)。两者都只缓存一次;首次运行后所有操作都是离线的。

要求

  • Node.js >= 22node:sqlite,被回退后端使用,从22版本起才稳定)。

  • 约950MB磁盘空间用于依赖项,约25MB用于嵌入模型,加上每个索引块约1-3 KB。

  • 无需GPU、无需外部服务、无需数据库服务器。

为什么是“轻量级RAG”

完整的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 live

add验证每个路径是否为真实目录,将其解析为绝对路径,并跳过重复项(包括通过符号链接到达的同一目录)。remove还会从索引中清除该文件夹的块,使其内容不再出现在结果中——如果希望将其从语料库中移除但保留可搜索性,请传递--keep-index

存储位置

配置和索引遵循XDG基础目录规范,因此它们在升级后仍然存在,并且被所有安装方法共享:

内容

位置

配置

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

索引 + 模型缓存

~/.local/share/semantic-search/

可以通过SS_CONFIG_PATHSS_INDEX_DIRSS_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字节的文件。要排除更多内容,请在以下任一位置放置一个gitignore风格的.indexignore文件:

  • 在你索引的文件夹内部 —— 模式相对于该文件夹,因此仓库可以排除自己的生成输出;

  • 在你的配置旁边~/.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?) —— 触发增量重新索引,以便代理无需shell即可刷新语料库。

所有搜索工具共享与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数组生成(从不使用shell),因此模式不能注入命令。

鉴于此,不要将其指向你不会交给LLM提供商的语料库——块会返回给任何请求它们的客户端。参见SECURITY.md

工作原理

分块

文本被分割成段落,然后贪婪地打包成约200个token的块,具有约35个token的重叠,使用嵌入模型的真实分词器而非字符计数近似值进行计数。这不是随意的:all-MiniLM-L6-v2有一个256 token的窗口,并静默截断任何更长的内容,因此块的大小被设计为适合该窗口,并为[CLS]/[SEP] token留出余量。重叠还被额外限制,使得重叠加上下一个段落永远不会突破该限制——否则块的尾部会在嵌入时被丢弃,同时仍由search返回。

单个段落大于硬限制(例如压缩包、一个巨大的日志行)会回退到基于单词的打包,使用相同的重叠逻辑,任何超过500个字符的单个“单词”会先被切片,因此不会有巨大的内容一次性交给分词器。

Token计数每个段落/单词计算一次并缓存,以便在重叠计算中重用。早期版本在每次重叠查找时重新分词,这在小型输入上没问题,但在大型仓库上导致CPU失控和内存增长数GB。如果你扩展分块器,请保留该属性。

增量重新索引

没有单独的清单——向量存储就是清单。每个存储的块都携带其源文件的mtimeMssha256内容哈希。每次运行时:

  1. 如果文件的磁盘mtime与存储的匹配,则跳过而不读取文件。

  2. 如果mtime更改但内容哈希相同(touch操作),则跳过重新嵌入。

  3. 否则,删除该文件的旧块并插入新嵌入的块。

  4. 遍历后,任何不再存在于磁盘上(且在被索引的根目录下)的索引路径都会被清除。

存储后端

默认是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 lint

检出根目录中的config.json优先于XDG位置,因此你可以在不影响真实设置的情况下针对临时语料库进行开发。测试始终写入临时目录。参见CONTRIBUTING.md

不在范围内(有意为之)

  • 答案生成。 这返回块,而不是答案。请自行将其输入LLM。

  • 使用第二个模型进行重排序。 词汇提升是一个廉价、无依赖的近似——不是真正的交叉编码器重排序器的替代品。

  • Web UI。 仅CLI和MCP。

  • 大规模语料库。 为个人或团队规模的文档和代码语料库构建——数万个块,而不是数百万。两个后端都假设该规模。

许可证

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