用途
rag-mcp 是一个最小化、诚实的“语料库检索”MCP 服务器,目标是为调用方提供可追溯的文档检索能力:把文档语料向量化后,针对查询返回带引用的段落,使答案有据可查。项目被设计为可嵌入 mcp-factory 的 manifest 模型。
主要能力
- 单一工具
search_knowledge(query, k, doc_class=None):嵌入查询、在本地语料中向量检索,返回带引用(source + heading + chunk_index)的段落。 - 完全本地、零费用嵌入:不依赖付费嵌入 API。
- 使用 ChromaDB 作为向量存储。
- 增量摄取(incremental ingest):默认增量。存储目录内的清单记录每个文件解码文本的 SHA-256;每次运行只重新嵌入实际变化的内容,并清理仅靠 upsert 无法处理的残留(已删除/重命名笔记的分块、变短笔记的尾部剩余分块)。
- 安全检查:
- 引用:每个命中都带 source + heading + chunk_index。
- 授权范围限定(Auth-scoped):结果被限制在配置的语料根目录内;越出该根目录的来源(绝对路径、
..遍历)会被拒绝。 - 软失败(Fail-soft):存储不可用或为空时返回结构化错误,绝不抛出会导致调用方智能体崩溃的异常。
- 有界:
k被限制在 [1, 20];空查询被拒绝。
- 依赖版本固定(requirements.txt)。
协议版本
固定于 mcp==2.0.0,这是首个实现 MCP 协议修订版 2026-07-28 的 SDK 版本。服务器在同一条 stdio 连接上同时服务两种协议时代,客户端的第一帧决定选择:2026-07-28 修订版无法通过 initialize 握手到达,它属于“现代”修订版,通过 server/discover 或内联的 _meta 版本标记到达。时代选择按连接自动进行,没有服务器端开关。tests/test_protocol_version.py 对两条路径进行端到端断言,因此导致服务器静默退回旧修订版的依赖回滚会在 CI 中失败,而不是悄悄通过。
保持索引新鲜(增量摄取)
增量摄取默认开启。存储目录内的清单记录每个文件解码文本的 SHA-256;一次运行只重新嵌入真正变化的内容,并清理仅靠 upsert 无法清理的内容(已删除/重命名笔记的分块,以及变短笔记的尾部剩余分块)。
在实时的 2808 个文件 / 26.6 MiB 语料上测量(bge,CPU):资料中未给出具体数值。
这正是让高频调度变得可负担的原因:reingest.bat 被设计为每 15 分钟运行一次,而非每天 03:00 一次——后者会让 03:05 写下的笔记在近 24 小时内对 search_knowledge 不可见。
只有当运行身份匹配时才会信任清单——嵌入器、嵌入维度、集合与分块参数。只要改动其中任何一项,每个文件都会被重新嵌入,因此嵌入器替换绝不会被悄悄半量应用。清单缺失、损坏或不匹配,或清单对应空存储,都会退化为完整重建;没有任何情况会退化为错误的跳过。
快照去重
清单的跳过是整文件哈希,因此无法识别真正伤害检索的重复:每日快照序列(如 fleet-health-2026-07-23.md 等)在文件内逐字重复昨天的段落,而文件哈希仍然发生了变化。在实时 vault 上测量,一条 ## RED Bots status 行在连续十五个文件中取了五个不同值,并以字节相同的副本挤占 top-10,把解释它的文档埋到第 16 位。
因此摄取还在分块层面去重,但仅限带日期的序列内,且仅与紧邻的上一个快照比较。第一次出现总是被嵌入,并保留自身日期作为其 source;之后的逐字重复不被嵌入,而是扩展存活者的 repeat_dates 元数据,search_knowledge 会以 snapshot_date / also_unchanged_on / snapshots_covered 返回。因此“某日期时它说了什么”仍可回答——这正是序列被去重而非排除的原因。发生变化后又回归的值会被保留,因为它是新事实而非重复。
范围很窄,规则写在 rag_mcp/snapshots.py:文件名以 -YYYY-MM-DD 结尾、同一目录与词干下至少有 3 个此类文件、在相同标题下字节完全相同。在实时语料上,这是 2814 个文件中的 316 个,合并了 50428 个分块中的 842 个(序列分块的 17.5%,全语料的 1.67%),且未触及任何普通笔记。可用 --no-snapshot-dedupe 禁用。
--full 就地重建(忽略清单,保留存储);--clean 先删除存储。两者仍会写入清单,因此下一次运行成本很低。reingest-clean.bat(每周)作为双保险重置保留。
作为 MCP 服务器使用
通过 mcp.yaml 注册(依据 mcp-factory 的 Manifest 加载器校验)。工具为 search_knowledge(query, k, doc_class=None);它读取由 RAG_MCP_* 环境变量配置的存储。
按文档类别过滤
每个分块的元数据都带有一个 doc_class,在摄取时设置。当文档的 YAML frontmatter 有 type: handoff 或 tags 条目包含 handoff(不区分大小写)时,它为 "handoff";或者——由于文档 frontmatter 是可选的,而这些会话镜像通常不携带任何 frontmatter——当文件直接位于 handoff_mirror_dir(默认 context/,可通过 ingest() 的 handoff_mirror_dir= / handoff_mirror_basenames= 参数或 CLI 的 --handoff-mirror-dir / --handoff-mirror-basename 标志配置)之下,且命名为 handoff.md / active.md / resume.md,或匹配一个锚定的 "handoff" 文件名 token(例如 handoff-2026-09-03.md、morning-dispatch-handoff.md)时——绝不使用裸子串匹配,因此标题仅提及该词的文档(handoff-skill-redesign-spec.md)仍保持为 "note"。其他一切默认为 "note"。传入 doc_class 可将查询限定到某一个类别,例如把智能体自身的会话/交接簿记排除在知识查询之外:
doc_class 是一个经过校验、区分大小写的枚举——"note" 或 "handoff"(见 rag_mcp.search.ALLOWED_DOC_CLASSES)——省略则不进行过滤。该集合之外的值(大小写错误、拼写错误、任何其他类型)会返回结构化的 invalid_doc_class 错误,与 invalid_query 形状相同。语法有效、但当前存储中恰好没有匹配项的 doc_class 仍会软失败为空的 ok: true 结果。
一次增量摄取运行会把 doc_class(以及任何其他元数据模式变更)回填到已嵌入、内容未变的分块上,而无需重新嵌入——见 ingest.CURRENT_METADATA_VERSION。完全早于该特性的存储,只需恰好一次增量运行就能让每个分块获得正确的 doc_class,而不是一次完整的 --clean 重建。
测试数量:这是收集到的总数,并由 CI 依据实时的 pytest 摘要强制校验(scripts/check_readme_counts.py,接入 .github/workflows/ci.yml)。有一个测试是平台条件性的——WINDOWS_ONLY 的 tests/test_reindex_handle_release.py 只在 Windows 上运行——因此 Linux CI 对同一套 264 个测试报告 263 passed, 1 skipped;门禁比较的是通过数加跳过数与声明值,而不是仅比较通过数,因此这个预期跳过不会被读作漂移。
商业支持
由 Jaimen Bell 维护。如需生产级 MCP 集成、定制服务器或智能体可靠性工作,见 jaimenbell.dev。
构建你自己的 MCP 服务器?MCP Starter Kit 提供模板、构建手册,以及发布此服务器时积累的打包实战经验。
限制与暂未提供
- 资料未给出具体定价,如需商用支持需自行联系 jaimenbell.dev。
- 资料未提供安装命令之外的运行示例、支持的文件格式清单、非英文语料表现、性能基准数值。
- 增量摄取的测量数据仅说明为“在实时的 2808 个文件 / 26.6 MiB 语料上测量(bge,CPU)”,未给出具体数值。