关于 compendio-mcp
compendio-mcp 将项目 Markdown 文档编入本地索引,通过 MCP 为 Agent 提供混合检索和按章节读取。它使用 SQLite 与本地嵌入模型,支持自动同步及多语言文档,无需查询时联网。
主要功能
- 01索引项目 Markdown 文档
- 02混合关键词与语义检索
- 03按章节读取文档内容
- 04自动同步文档变更
- 05支持多语言本地搜索
适用场景
- 01快速定位项目文档中的相关段落
- 02检索多个文档目录中的规范与提案
- 03减少向 Agent 提供整篇文档的需要
MCP 相关
它作为本地 MCP 服务提供文档概览、片段搜索和文档读取三个工具。MCP 客户端可逐步从语料概览转向搜索,再读取所需章节。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
3收录的工具列表 · 更新于 2 周前
此工具无需参数。
详细介绍
以尽可能少的令牌,将项目文档提供给任意 Agent。
作为 MCP 服务公开的本地 RAG 检索层。你的 Agent 不再使用 grep 搜索并倾倒整个文件,而是直接找到相关段落。
功能介绍 • 要求 • 快速开始 • 配置 • MCP 工具 • CLI • 工作原理 • 增量同步 • 多语言 • 完整文档
问题
你的 Agent 不知道你的文档。因此它只能尽力而为:先用 grep 搜索,再用 cat 读取一个 400 行的文件,只为回答一个答案就在某个段落里的问题。查了三个文件后,上下文窗口里塞满了噪声,答案依然只是猜测。
附加整个 docs/ 文件夹并不能解决问题——只不过是把浪费提前了。关键字搜索也无济于事:没人会用文档中的确切措辞来提问。
相同的问题、相同的模型,两个答案都正确——区别在于得到答案的成本。
6 次工具调用 • 29 秒 • 32,301 个令牌 → 2 次工具调用 • 12 秒 • 17,684 个令牌。
数据取自 OpenCode 自带的耗时标签和费用提示框,语料库包含 81 份文档。
Compendio 为 Markdown 文档建立索引,并为任意 AI Agent 提供 3 种工具,让它们精准查找和阅读所需内容。
- 🔍 混合检索,而非 grep — 关键词搜索可找到确切术语,语义搜索可找到改述内容。Compendio 会同时运行两种搜索并合并结果。
- ✂️ 设计上节省词元 — 浏览每篇文档约需 10 个词元,搜索只需查找少量片段,阅读时只需读取一个章节。无需读取整个语料库。
- 🔒 100% 本地运行 — 只需一个 SQLite 文件,在 CPU 上生成嵌入,查询时零网络调用。无需 API 密钥、Docker 或服务,任何数据都不会离开你的设备。
- ♻️ 保持最新 — 运行中的服务会自动检测文档修改。无需监视进程,也无需手动反复重建索引。
- 🗣️ 支持多语言 — 可为任何语言的文档建立索引。嵌入模型支持多语言,搜索也不区分变音符号。请参阅多语言。
- 🧩 零配置 — 适用于任何包含
.md文件的文件夹。无需必填的 frontmatter,也无需配置文件。如果你的团队已有需要强制执行的分类体系,也可以选择使用文档约定。
要求
- Node.js ≥ 22.12。
- 无其他要求。
快速开始
1. 安装。
npm install -g compendio-mcp
更新现有安装:
compendio update
2. 在客户端中将其注册为 MCP 服务:
compendio install-mcp <agent>
支持的 Agent:claude、claude-desktop、cursor、vscode、opencode、codex、zed。
如需手动安装,或要用于此命令不支持的 Agent(Windsurf、Cline、Gemini CLI),请参阅手动安装。
3. 在项目根目录中构建一次索引:
compendio index
就这么简单。无需配置文件,Compendio 会自动发现包含 Markdown 文件的顶层文件夹,无需隐藏的 docs/ 默认目录。将 .compendio/ 添加到你的 .gitignore。
为什么需要这一步。 服务也会在启动时建立索引,所以严格来说,你可以跳过这一步——但首次运行会下载并缓存嵌入模型(几十 MB),触发下载的人需要等待。在这里运行,就能在终端中支付这笔开销,并看到进度条,而不必等到 Agent 首次调用工具时才开始。从那以后,所有操作都会离线进行,索引也会自动保持最新(见下文)。
Windows 注意事项。 某些 MCP 客户端无法直接启动
compendio.cmd垫片。如果服务因ENOENT而无法启动,请使用"command": "npx"和"args": ["compendio-mcp", "serve"]。
配置
完全可选——即使没有配置文件,Compendio 也能正常工作。只有在需要覆盖设置时,才在项目根目录创建 compendio.config.json:
{
"docsDir": ["docs"],
"exclude": ["INDEX.md"],
"db": ".compendio/compendio.db",
"embeddings": { "provider": "local", "model": "Xenova/multilingual-e5-small" },
"chunk": { "minTokens": 100, "maxTokens": 480 },
"search": { "k": 5 },
"sync": { "throttleMs": 30000 },
"convention": {
"mode": "loose",
"excludedStatuses": [],
"frontmatterFields": { "type": "type", "module": "module", "status": "status" }
}
}
| 键 | 用途 |
|---|---|
docsDir | 一个或多个明确指定的文档根目录,相对于项目根目录。始终是数组,不支持单个字符串形式。省略此项或设为 [],即可使用发现模式 |
exclude | 索引时跳过的条目:精确路径、裸文件名(在任何位置匹配),或目录前缀(例如,"adr/superseded" 会跳过其下的所有内容) |
db | SQLite 索引文件的写入位置 |
search.k | 每次搜索默认返回的片段数 |
chunk | 片段大小范围,以令牌数为单位 |
sync.throttleMs | 自动同步轮次之间的最短间隔,以毫秒为单位(30000 = 30 秒)。这是一个下限,而非计时器——仅限制 serve 的自动触发,不限制手动运行的 compendio sync——参见增量同步 |
convention | 可选的文档分类体系——见下文 |
只声明 convention 块中的部分字段时,会逐字段与默认值合并;未提及的其他字段不会被清除。frontmatterFields 可将 type/module/status 映射到非标准的 frontmatter 键(例如,{ "status": "estado" } 会将西班牙语文档中的 estado: 字段作为 status 读取)。
每个数值键(search.k、chunk.minTokens、chunk.maxTokens、sync.throttleMs)只有在值为大于 0 的有限数时才会生效——search.k 还必须是整数。其他任何值,包括 "480" 这样的带引号数字,都会回退到默认值,效果与未设置该键完全相同,并会报告回退情况:每个 CLI 命令都会将其输出到 stderr,MCP 客户端则会在 docs_overview 的响应中收到报告。embeddings、chunk 或 convention.frontmatterFields 下无法识别的键(例如拼写错误的 maxtokens)也会以相同方式报告。配置无误时不会输出任何报告。
多个文档根目录
声明多个根目录,即可将多个文件夹(如 adr/、rfcs/ 和规范目录)编入同一个可搜索语料库:
{ "docsDir": ["docs", "openspec"], "exclude": ["INDEX.md", "openspec/changes/archive"] }
每个文档的 path 都会添加其根目录的别名作为前缀——别名就是目录本身的名称,因此 docs/x.md 和 openspec/specs/y.md 都会显示为真实的项目相对路径。单个显式根目录和自动发现的根目录也遵循这一规则:路径为 openspec/specs/y.md,而不是 specs/y.md。search_docs、docs_overview、read_doc 和生成的 INDEX.md 都使用这种带前缀的格式;将返回的 path 原样传给 read_doc,始终可以正确解析。
声明的根目录不能冲突:两个根目录解析到同一目录、一个根目录嵌套在另一个根目录中(无论声明顺序如何),或两个根目录具有相同的目录名(因而别名相同),都会在编入任何内容之前被拒绝。已声明但无法读取的根目录(例如路径拼写错误,或某些检出版本中才有的文件夹)会被报告并跳过;同步会继续处理其余根目录,只有当所有已声明的根目录都失败时才会抛出错误。从 docsDir 中移除某个根目录后,下次同步时会删除其中的文档,效果与删除这些文件本身相同。
在自动发现模式下,Compendio 会在每次 index、sync 和 serve 同步时重新扫描顶层文件夹,选取其任意子目录中包含 .md 文件的文件夹,跳过符号链接内容以及 .git、.compendio、node_modules、dist、build 和 coverage 等生成目录或内部目录,并在项目根目录写入 INDEX.md。自动发现采用失败即关闭策略:顶层配置 JSON 格式错误、候选目录树无法读取、遍历或读取失败,或先前已编入索引的自动发现根目录在同步前消失或变成符号链接/联接点,都会在修改索引之前中止。如果先前已编入索引的根目录仍是可读取的目录,即使其中最后一个 Markdown 文件已被删除,也仍会遍历该目录,以便正常处理文件删除。符号链接检查会在扫描/遍历时使用 lstat/realpath,但这并非内核级沙箱;检查与读取之间的文件系统竞态不在处理范围内。--dir <path>(见下文)表示显式模式:它会用指定目录替换整组已声明/自动发现的根目录,并在该目录内写入 INDEX.md。
兼容你的 SDD 框架
规范驱动开发框架会将规划产物保存在 Markdown 中,而这正是 Compendio 会编入索引的内容。将 docsDir 指向框架写入文件的文件夹:
| 框架 | 配置 | 索引内容 |
|---|---|---|
| Spec Kit | { "docsDir": ["specs", ".specify"] } | specs/NNN-feature/ 下的功能规范、计划和任务,以及 .specify/memory/constitution.md 中的项目章程 |
| OpenSpec | { "docsDir": ["openspec"] } | openspec/project.md、能力规范和进行中的变更 |
| Kiro | { "docsDir": [".kiro"] } | .kiro/specs/ 下每项功能的 requirements.md/design.md/tasks.md,以及 .kiro/steering/ |
| BMAD | { "docsDir": ["docs"] } | PRD、架构、拆分后的史诗和用户故事 |
| 框架 + 你自己的文档 | { "docsDir": ["docs", "openspec"] } | 两者都会作为一个可搜索语料库 |
.specify/ 和 .kiro/ 等隐藏目录会照常编入索引,无论它们是声明的根目录还是通过发现模式找到的目录——会被跳过的是根目录内部以点开头的条目,而不是根目录本身。因此,即使完全没有配置文件,Spec Kit 或 Kiro 项目也已能编入索引。
复制配置前,值得了解以下三点:
- 根目录的别名取自其目录名,而非你声明的路径。
.kiro/specs的别名是specs,因此其中的文档会以specs/auth/design.md的形式返回。这也意味着它会与顶层specs/根目录冲突,不能与 Spec Kit 一起使用——应改为声明.kiro,正如表格中的配置。 - BMAD 的输出文件夹可配置。
docs是默认值;BMAD v6 会从自己的配置中读取output_folder,因此应声明实际设置的目录。 - 模板会产生干扰。
.specify/templates/中存放的是占位脚手架,而不是项目知识。如果你不希望它们出现在搜索结果中,可以添加"exclude": [".specify/templates", ".specify/scripts"]。
文档约定(可选)
通过 convention.mode 选择两种模式:
loose(默认,无需配置)——不会因文件缺少元数据而拒绝文件。标题取自首个 H1(若没有则使用易读的文件名),模块根据文件夹推断;如果存在 frontmatter,则读取type/status,否则不设置。strict(可选启用)——一种检查器:每份文档都必须包含 H1,以及非空的type/module/status,并根据项目声明的列表进行验证。未通过检查的文件会被跳过并报告,但不会导致运行中断。
{
"convention": {
"mode": "strict",
"types": ["functional", "adr", "api", "qa", "guide"],
"statuses": ["draft", "current", "deprecated"],
"excludedStatuses": ["draft", "deprecated"]
}
}
excludedStatuses 会按生命周期状态将文档从搜索结果中隐藏——草稿和已弃用页面不会再干扰结果。有关本仓库自身文档所遵循的完整约定,请参阅 docs/documentation-convention.md。
MCP 工具
采用渐进式披露设计:低成本地了解概况 → 低成本地搜索 → 只读取所需内容。
1. docs_overview() — 语料库概览。按类型和模块统计,并为每份文档提供一行说明。每份文档约 10 个令牌。
2. search_docs({ query, type?, module?, tags?, k?, include_excluded? }) — 返回排名靠前的 k 个片段(默认为 5 个,每份文档最多 2 个),每个片段包含路径、章节、摘录和得分。type 是由项目自行定义的开放字符串,并非固定列表。
3. read_doc({ path, section? }) — 读取一个章节或整份文档。对于包含章节且较大的文档(估算令牌数超过约 6,000),会返回精简的 H2/H3 标题大纲,而不是正文,让 Agent 只读取所需章节。若路径不存在,则会返回最相似的 3 个路径,而不是报错,让 Agent 自行纠正,无需盲目重试。
CLI
| 命令 | 功能 |
|---|---|
compendio serve | 通过 stdio 启动 MCP 服务 |
compendio install-mcp <agent> | 在指定 Agent 的配置中注册 compendio-mcp |
compendio update | 将 compendio-mcp 更新到 npm 上的最新版本 |
compendio index | 完整重建索引 |
compendio sync | 从终端运行一次增量同步——仅同步内容发生变化的文档,并实时显示进度。请参阅增量同步 |
compendio search "..." | 支持筛选条件的混合搜索:--type、--module、--tags、-k、--all |
compendio overview | 已索引语料库的概览 |
compendio index-md | 生成或更新一个合并的 INDEX.md:在发现模式下生成项目根目录中的 INDEX.md,或在第一个显式指定的根目录/--dir 根目录中生成 INDEX.md——每份文档占一行 |
compendio eval | 根据 goldenset 衡量检索质量 |
全局选项 -C, --root <dir>:项目根目录。在 index、sync 或 search 中添加 --lexical,可完全跳过嵌入。index/index-md 上的 --dir <path> 会用该目录替换已配置的 docsDir——不会将其添加进去,且生成的索引仍采用带前缀的路径形式(<dirname>/x.md)。sync 没有 --dir:在增量同步过程中,用这种方式移除根目录会删除其中的文档,而不只是跳过它们(请参阅 compendio sync --help)。
工作原理
docs/**/*.md
│
├─▶ split into fragments at heading boundaries, then bounded to maxTokens
│
├─▶ index each fragment twice ─┬─ full-text (keywords)
│ └─ embeddings (meaning)
│
└─▶ one file: .compendio/compendio.db
在查询时,系统会分别搜索两个索引,并通过 倒数排名融合合并排名——这是一种基于排名的合并方式,无需盲目调试权重。Agent 会收到数量最少且相关的片段。
Compendio 负责 RAG 中的检索部分。它从不调用大模型,也不会生成任何内容:它会找到正确的段落,然后不再介入。
如果嵌入模型不可用,Compendio 不会崩溃,而是降级为仅关键词搜索,并在响应中说明这一点。
增量同步
你工作期间,文档会不断变化,Compendio 会自动跟上,也可以按需同步。索引有四种刷新方式:
| 触发方式 | 执行内容 |
|---|---|
服务启动(compendio serve) | 执行一次增量同步,在传输连接前启动。首次工具调用会等待同步完成,因此绝不会基于尚未加载的索引作答 |
任意 MCP 工具调用(search_docs、docs_overview、read_doc) | 执行一次增量同步,但仅当距离上次同步已过 30 s(默认 sync.throttleMs 为 30000)时才会执行。否则,调用将基于当前索引继续处理 |
compendio sync | 从终端手动运行一次增量同步,并显示实时进度。sync.throttleMs 不会限制此命令——无论距离上次同步过去多久,每次调用都会执行一次全新的同步。建议在一次性添加大量文档后运行此命令。 |
compendio index | 从头开始完整重建:删除索引并重新创建 |
它不是定时器。 没有后台定时任务,也没有文件监视器。同步由你的 Agent 发起工具调用或由你运行 compendio sync 来触发;节流间隔是两次自动触发之间的最短间隔,而非执行计划:无人查询的服务不会同步,而一秒内突发的十次调用最多也只会触发一次同步。并发调用会加入正在运行的同步,而不是另行启动一次。
每次增量同步都会将内容哈希与已建立索引的内容进行比较,因此只有新增、更改和删除的文档需要处理——未更改的语料库不会产生任何开销。在 serve 中,如果同步失败,会将错误记录到 stderr,但工具仍会基于当前索引作答;compendio sync 没有这种回退机制,因此同样的失败会导致进程以非零状态码退出。
何时需要完整重建。 compendio index 是唯一会重新建立索引的命令:它会删除并重新创建整个数据库,也是权威的重建方式。在大规模重组后、怀疑索引已偏离实际情况时,或者——这个情况常常令人意外——更改 chunk.minTokens/chunk.maxTokens 后,都应使用它。无论是自动执行还是手动运行 compendio sync,增量同步都只根据内容哈希来识别文档,因此未编辑的文档会保留旧的片段边界,无论当前配置如何。只有完整重建索引(compendio index)才会对未更改的文件应用新的分块设置——请参阅 compendio sync --help,其中也会在最可能需要了解这一点时提醒你注意。
多语言
使用团队日常工作的任何语言编写文档即可,Compendio 不会介意:
- 约定使用英语,语料库则不必如此。 工具参数(
path、type、module、tags、section)、响应字段和工具描述都使用英语,因此任何 Agent 都能轻松读取。文档使用哪种语言与此无关:索引前会移除 frontmatter 键,而 FTS5 分词器不带特定语言的词干提取器。 - 非英语 frontmatter 键会映射回对应字段。 如果文档使用
estado:而非status:,convention.frontmatterFields会将其转换。 - 重音符号会得到妥善处理。 搜索不区分变音符号,因此 validación 和 validacion 会匹配。区分重音的搜索会悄无声息地漏掉结果。
- 嵌入模型支持多语言(
Xenova/multilingual-e5-small),因此单语言和混合语言语料库的索引与检索效果相同。
ejemplos/ 中附带的参考语料库和评估集使用西班牙语——这是有意为之,用以证明英语代码库和工具约定也能无损检索非英语文档。
与 grep 相比,语义检索提升了多少?
使用 compendio eval 在示例语料库(ejemplos/:11 份文档、29 个分块、没有配置文件——即零配置路径本身)及其包含 22 个真实问题的黄金集上进行测量:
| mode | recall@5 | MRR | failures |
|---|---|---|---|
| hybrid | 1.00 | 0.943 | 0 |
| keyword-only | 0.95 | 0.856 | 1 |
- 当问题使用语料库中的术语时,关键词搜索已经表现出色。
- 差距体现在改述和同义词上:«¿Qué endpoint hay que llamar para crear un lead?» 在没有嵌入的情况下会落到前 5 名之外,而语义检索会将其找回。与匹配文档完全没有词汇重叠的问题,只能靠语义检索解决。
- 速度:模型预热后,混合搜索的响应时间为 5–20 ms。
compendio eval 可随时复现此表格;它也是调整分块和 k 的工具,无需猜测。
架构
六边形架构:核心不依赖 SQLite、transformers.js 或文件系统。
src/
├── domain/ # pure, no dependencies: model, chunking, ranking, convention policy
├── application/ # use cases
├── infrastructure/ # adapters: SQLite, markdown parsing, filesystem, embeddings
├── composition.ts # composition root — start here to see the whole app
├── cli.ts # input adapter: commander
└── server.ts # input adapter: MCP server (stdio)
所有外部依赖都位于 src/domain/ports.ts 中的端口之后。替换向量存储或嵌入提供方,只需在一个适配器中局部修改,无需重写。
开发
npm install
npm run build # compiles to dist/
npm test # vitest: domain, adapters and integration
npm run typecheck # tsc --noEmit
npm run dev -- ... # CLI without compiling (tsx)
集成测试使用确定性嵌入提供方(无需下载),并针对真实的 ejemplos/ 语料库运行。
无需安装此软件包,即可使用 CLI 测试随附的示例语料库:
node dist/cli.js --root ejemplos index
node dist/cli.js --root ejemplos search "¿cuándo se considera duplicado un lead?"
此仓库附带一个 .mcp.json,用于提供 ejemplos/ 语料库,因此无需任何配置,就能通过 Claude Code 试用这些工具。
许可证
MIT © Raúl García Barciela
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。