Skip to main content
Glama
mmorrisj
by mmorrisj

corpus-mcp

一个 MCP 服务器,让智能体可以对文档目录进行关键词搜索。指向一个文件夹即可使用——无需下载模型、无需 API 密钥、无需 GPU、无需并行运行向量数据库。唯一依赖:MCP SDK。

pip install -e .
corpus-mcp --root ./docs serve

有趣的部分不在于检索,而在于工具设计:智能体实际上能用搜索工具做什么,以及是什么让一个工具可用,而不是成为上下文窗口的焚化炉。


十秒试用

$ make demo
1. reference/glossary.md  (score 1.973, f700ededcfdd:0)
   # Glossary

   **Extraction** — the process of dissolving soluble compounds out of ground
   coffee. Under-extraction tastes sour and thin; over-extraction tastes bitter …

2. guides/brewing.md  (score 1.774, 71c6f092dbcb:0)
   # Pour-over brewing
   …

那个查询是 "why does my coffee taste sour"。文档说的是 tastes,查询说的是 taste,而真正解答问题的术语条目排在第一位。这两点都是有意为之;详见下文。

Related MCP server: Saga

工具

工具

用途

search(query, limit, snippet_chars)

按相关性排序的段落片段,简短且以匹配为中心,每个片段带 chunk_id

fetch(chunk_id, context_chunks)

某一个片段的完整文本及其相邻片段

list_sources(limit)

已索引的内容,以及每个文档的大小

文档还作为 MCP 资源 暴露在 corpus://<relative-path>。

值得商榷的设计决策

Search 和 fetch 是独立的工具。 一个直接返回完整块的 search 写起来更简单,但用起来糟糕得多:十条各 1200 字符的结果,在智能体决定想要哪一条之前,就已经消耗了大半上下文窗口。因此 search 返回片段——足以初步筛选——而 fetch 按需展开选中的结果。智能体只在自己认为值得的地方为细节付费。

片段以匹配为中心,而不是块的开头。 返回前 N 个字符的做法经常失败,因为匹配的句子通常在中部:智能体看到一段无关的前言,要么放弃一个好结果,要么为了弄清楚而抓取全部内容。片段窗口的选择会尽量覆盖尽可能多的查询词出现位置。

所有限制都在服务端强制实施。 工具输出直接进入上下文窗口,所以无界工具就是对调用它的东西进行拒绝服务攻击。调用方要求 10000 条结果正是上限存在要应对的情况,因此限制是被强制执行的,而不是被信任的。当输出被截断时,响应会明确说明,这样智能体可以缩小查询范围,而不是假设自己看到了全部内容。

空结果会自我解释。 一个光秃秃的空列表就是死胡同。响应会报告存在多少个块和文档,从而区分“你的查询没命中”和“什么都没索引”——这两种情况的下一步不同。

过期标识符是预期结果,而不是错误。 文档被编辑时块 id 会变化,因此长会话中较早出现的 id 可能失效。fetch 会明确说明这一点,并告诉智能体重新搜索。

块合并时会去除重叠。 块之间有重叠是为了不让任何段落被边界切断,但把重叠交还给智能体意味着它会读到重复的句子,并可能把重复当作强调。块携带绝对偏移量,因此重叠是按位置而不是按字符串匹配去除的。

使用 BM25,而非嵌入。 对于智能体在浏览它已经有所了解的语料库时发出的关键词式查询,词法检索足够强,而且它具备在智能体循环中最关键的性质:快速,且永远不会悄悄花钱。语义搜索是有价值的补充,而不是让这个东西有用的前提。

轻度词干化,而非真正的词干分析器。 复数和常见动词词尾会被归并,使 tastes 能匹配 taste。完整的 Porter 实现有上百行代码,也是一个维护面,而且它的长尾(operational → oper)在短查询上很可能弊大于利。索引和查询共用同一个分词器,因为两者之间的任何分歧都会悄悄损害召回率。

安全

服务器指向一个根目录,并且永远不会读取它之外的内容。这比看起来更重要:工具参数来自模型输出,因此文档标识符是不可信输入,../../.ssh/id_rsa 是一个困惑或有对抗性的智能体最终会要求的东西。

每一条跨越边界的路径都要经过一次隔离检查,该检查在比较之前先解析符号链接——根目录内指向外部的符号链接会绕过对未解析路径的前缀检查。看起来像绝对路径的参数会被解释为相对于根目录,而不是真正的绝对路径。资源 URI 与工具参数受到同等对待。

非 UTF-8 文件、超大文件和厂商目录(.git、node_modules 等)会被跳过,而不是作为噪声编入索引。

连接到客户端

Claude Desktop 或任何 MCP 主机都会以子进程方式启动服务器:

{
  "mcpServers": {
    "my-docs": {
      "command": "corpus-mcp",
      "args": ["--root", "/absolute/path/to/docs", "serve"]
    }
  }
}

语料库在磁盘上发生变化时会重新读取,因此会话期间编辑的文件无需重启就能被搜索到——重新索引按修改时间增量进行,而不是每次调用都重建。

开发

make install   # server plus dev tools
make demo      # one query against the example corpus
make test      # 89 tests, no network required
make smoke     # launch the installed server as a subprocess and exercise it
make lint

两层测试,因为它们捕获不同的失败:

  • tests/test_server.py 在进程内用真实的 MCP 客户端驱动真实的服务器。 被测试的是线路行为——工具模式、结构化结果、错误形状——而不是底层的 Python 函数。一个函数正确但工具表面错误的服务器仍然是坏的,只有这一层能捕获这类问题。

  • scripts/stdio_smoke.py 将已安装的控制台脚本作为子进程启动,并通过 stdio 与它进行 JSON-RPC 通信,就像主机那样。这覆盖了打包、入口点和传输层——包括经典的“有东西写到 stdout 并破坏协议流”的失败。

局限

  • 仅限词法检索。 与文档没有任何共同词汇的查询将无法找到它。在同一工具表面之后添加嵌入后端是显而易见的下一步。

  • 仅限文本格式——.md、.txt、.rst、.csv、.json、.yaml 等。不支持 PDF 或 DOCX 提取。

  • 整个索引驻留在内存中,语料库变化时会完整重建。对于为此构建的数千文档场景没问题;百万级语料库需要一个按文件更新的真正索引。

  • 仅支持英语。 停用词表和词尾归并都基于英语。

  • 除根目录外没有访问控制。 根目录下的每个文件对服务器连接的任何对象都可见。

许可证

MIT。由 Aion Innovations 构建。

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    An MCP server that allows users to efficiently search and reference user-configured documents through document listing, grep searching, semantic searching with OpenAI Embeddings, and full document retrieval.
    4
    3
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    A local-first MCP server for document ingestion and semantic search, providing tools to add, search, and retrieve documents, chunks, and code blocks.
    13 npm
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    A local-first MCP server providing secure workspace file operations, offline full-text search, and web search/fetch capabilities without requiring API keys.
    10
    -
  • A
    license
    A
    quality
    A
    maintenance
    A self-hosted MCP server providing web search and URL fetching tools, running locally without external API keys or accounts.
    2
    152 npm
    MIT