用纯 C 写 MCP 服务器,codebase-memory 为什么连一个 LLM 都不肯内置

发布于 · 2,673 字 · 约 7 分钟#Github 解读#Agent 基建原文链接
用纯 C 写 MCP 服务器,codebase-memory 为什么连一个 LLM 都不肯内置 封面图
  • 将代码库索引为持久化 SQLite 知识图谱,支持毫秒级 Cypher 图查询
  • 不内置 LLM,利用客户端 Agent 作为自然语言到图查询的翻译器
  • 纯 C 编写实现零运行时开销,3 分钟索引 Linux 内核 2800 万行代码
  • 内存优先策略导致大仓库可能 OOM,提供内存预算环境变量控制
  • 支持团队协作导出压缩图谱制品,增量补齐避免重复索引

你盯着屏幕问 Agent 一个问题,谁调用了 ProcessOrder。它开始干活,先 grep 一轮,捞出十几个文件,然后一个一个 Read,翻到第七个文件的时候 token 已经烧掉六万,答案还没影。

这不是 Agent 笨,是它的工作方式本身就贵。代码的结构信息,查一次表就能拿到,它非要现场把原文读一遍。

2026 年 2 月 24 日,GitHub 上冒出来一个项目 codebase-memory-mcp,思路很直接,把整个代码库预先索引成一张知识图谱,函数、类、调用链、HTTP 路由全部变成图上的节点和边。Agent 再问谁调用了 ProcessOrder,一次图查询,1 毫秒内出结果。六个月后的今天,39738 个 Star,3195 个 Fork。

先用一句话把它钉住

codebase-memory-mcp 是一个用纯 C 写的 MCP 服务器,把代码库索引成持久化的 SQLite 知识图谱,给 AI 编程 Agent 当结构查询后端。15 个 MCP 工具,158 种语言,单个静态二进制,不需要任何语言运行时和 API key,下载、install、重启 Agent,完事。

它不带 LLM。

这一点后面细说,是整个项目最值得琢磨的决策。

为什么偏偏是 C

说真的,AI 圈的工具今年清一色 TypeScript 和 Python,一个 MCP 服务器用 C 写,第一眼像是走错了片场。但把性能表拉出来,你就能看懂这层选择。

Linux 内核,2800 万行代码、75000 个文件,全量索引 3 分钟,产出 481 万节点、772 万条边。Django 这种规模,6 秒。Cypher 图查询,1 毫秒以内。死代码检测,全图扫描,150 毫秒左右。其实吧,单看每一条都还有压缩空间,但放在一起,这就是脚本语言给不出的响应曲线。

这些数字的前提是索引全程跑在内存里,LZ4 压缩读取、内存态 SQLite、最后一次性落盘。C 在这条路线上的优势很朴素,没有运行时开销,没有 GC 停顿,内存想要多少自己说了算。

源码结构也能看出这是个「自己动手」的项目。src/ 下 209 个文件,foundation/ 目录自己造了一套地基,arena.c 内存池、hash_table.c、compat_fs.c 跨平台文件系统兼容层,一应俱全。往上走,src/mcp/mcp.c 一个文件 518KB 装下整个 MCP 服务器,src/store/store.c 320KB 是全部图存储,src/cypher/cypher.c 180KB 塞进一个 Cypher 引擎的词法、解析、规划、执行四层。

坦白讲,这种巨型单文件在工程上不算优雅,但它的模块边界划得很硬。store.h 里 typedef struct cbm_store cbm_store_t,不透明句柄,调用方永远摸不到 SQLite 内部,所有函数统一 cbm_store_ 前缀。粗中有细。

两层解析,tree-sitter 打底,Hybrid LSP 补刀

整个项目的技术核心,是它怎么把代码变成一张准的图。

第一层是 tree-sitter,158 种语法全部编译进二进制,vendor 进 internal/cbm,1558 个文件。tree-sitter 快是真快,但它只给语法树。user.profile.display_name() 解析到 MethodCall 就停了,它不知道这个方法定义在三个模块之外的 Profile 类里,因为 tree-sitter 不追踪 import、泛型、继承这些语义信息。

于是有了第二层,作者管它叫 Hybrid LSP,用 C 手写的类型解析算法,思路对齐 pyright、tsserver、gopls、rust-analyzer 这些主流语言服务器,做参数绑定、返回类型推断、泛型替换、JSX 组件分发。覆盖 Python、TypeScript、Go、Rust、Java、Kotlin、C/C++、PHP、C#、Perl 共 11 种语言。没有 Hybrid LSP 的语言退回文本解析,保证至少有个答案。讲真,这层是最见功力的地方,解析得准,图上的调用链才值得信,解析偏了,整张图就开始骗你。

管线是多 pass 流水线。src/pipeline/ 下二十多个 pass_*.c,pass_definitions 先抽定义,pass_calls 再连调用边,pass_lsp_cross 做跨文件类型解析,pass_k8s 把 Kubernetes 清单和 Dockerfile 也编进图,pass_similarity 用 MinHash 找近似重复代码。一轮跑完,图才算完整。

系统架构图
系统架构图

从上往下看,Agent 从 MCP stdio 前端进来,daemon 层管会话协调,索引管线逐 pass 建图,解析引擎在底层托着 tree-sitter 和 Hybrid LSP,最后全部落进 SQLite 图存储和 Cypher 查询引擎。整个 src/ 209 个文件,每一层都有活干。

增量这块做得挺讲究。store.h 里的 cbm_lsp_surface_row_t 结构,每个文件持久化自己的 LSP 定义集,还带一个 ref_bloom 布隆过滤器记录引用过的标识符,文件变了就沿着引用闭包做定向修复,不用全量重建。注释里也写了老实话,数据库里没有 surface 数据的旧项目,直接走全量重建路线。

RAM-first,快是真快,另一面也是真硬

内存优先这条路线,让它 3 分钟能吃下 Linux 内核,也让它在超大仓库上结结实实翻过车。

issue #1654,23 条评论还开着。一台 376 GB 内存、96 核的机器,v0.9.0 索引同一个 574 万抽取单元的仓库,13 分钟搞定。升到 v0.10.4,跑了 45 分钟完成度 1.2%,mimalloc 分配失败,SIGABRT 直接崩掉。README 把 RAM-first 写在卖点栏,这条 issue 就是它的另一面。

作者留了后手。CBM_MEM_BUDGET_MB 环境变量可以手动压内存预算,CBM_DUMP_VERIFY_MIN_RATIO 默认 0.5,落盘节点数低于内存提交数一半,就返回 degraded 状态而不是假装成功。这个 0.5 的阈值挺诚实,也挺吓人,你想想看,落盘丢了将近一半以内,都算「可接受」。

高 CPU 的抱怨也没断过。#45 从 3 月开到现在,用户在 MacBook Pro M4 上同时开三个仓库,后台 watcher 的 CPU 一直下不来。Windows 那边更热闹,#394 专门开了个 task 追 8 个平台 bug,#221 里 Windows 11 用户装不上 opencode 集成,17 条评论。

不装 LLM,是整个项目最聪明的一步

你想想看,同类代码图谱工具不少往里嵌 LLM,做自然语言到图查询的翻译。代价是 API key、额外成本、多一个要配置的模型。

这个项目的判断反着来。MCP 客户端,Claude Code 也好 Codex 也好,本身就是个 LLM,让它当查询翻译器,后端只管把图查得快。README 里写得干脆,the agent you're already talking to is the query translator,你正在对话的这个 Agent,就是查询翻译器。

于是分工变成,你问谁调用了 ProcessOrder,Agent 自己决定调 trace_path(function_name="ProcessOrder", direction="inbound"),后端毫秒级返回结构化结果,Agent 再组织成人话给你。智能留在客户端,速度留在本地。

对比一下就更清楚了。基于 LSP 的 Serena 每个项目起一套语言服务器进程,Aider 的 repo map 每次现场计算,graphify 这类工具输出目录再解析。codebase-memory 的差异点就两个词,持久化,加免进程。图谱建一次存 SQLite,查询不依赖任何语言服务器活着。

生态铺得比想象中野

install 命令一口气配置 43 个客户端面,从 Claude Code、Codex、Cursor 到一堆我叫不上名字的 CLI 工具。每个面装三层代理,Scout 做快速试探,Verify 是默认档做证据核查,Auditor 管边界审计。安全边界也讲了原则,不碰实验性开关,不启用 YOLO 模式。

后台跑一个跨会话协调 daemon,多个 Agent 会话共享同一套 watcher 和索引任务,谁起的服务谁关,最后一个会话退出才收摊。还内置一个 3D 图谱可视化,localhost:9749 打开就能玩。

团队协作的设计我觉得最实用。图谱能导出成单个 zstd 压缩文件提交进仓库,.codebase-memory/graph.db.zst,压缩比 8 到 13 比 1,同事克隆下来直接从制品增量补齐,不用各自从头索引。.gitattributes 自动加一行 merge=ours,避免二进制制品的合并冲突。不想要就整个目录 gitignore,各回各家。

更新机制也有点反常识的讲究。二进制自己不发网络请求,update 命令只打印出该跑的安装脚本路径让你自己执行。作者的解释是,进程内更新器结构上就是个下载器,为了几个月跑一次的命令往每个二进制里塞这套东西,不划算。

一个人扛着 39.7K Star 往前跑

贡献者数据值得单独看一眼。第一名 DeusData,1345 次贡献,第二名 53 次。39.7K Star 的项目,实际是一个人加一群偶尔提 PR 的人。bus factor 低到贴地。

发布节奏也猛。8 月 11 日到 19 日,9 天发了 8 个版本。修 bug 快是好事,但 v0.10.4 把 v0.9.0 能正常索引的大仓库搞崩这种事,也说明快有快的代价,回归就是在这种节奏里溜进来的。

安全上倒是下足了功夫。每个 release 的三个二进制变体都送 VirusTotal 扫描,SLSA Level 3 构建溯源,cosign 签名,全链路可审计。起因有点无奈,Microsoft Defender 会把这个二进制误报成 Trojan:Script/Wacatac.B!ml,作者干脆把整套验证流程做成了公开证据链,release notes 里每个二进制都挂着 0/72 的扫描链接。

哑引擎,聪明客户端

拆完这个项目,我带走的是一个可复用的分工模式,哑引擎加聪明客户端。

工具侧只做两件事,把索引建得快,把查询答得快。语言理解、意图翻译、结果组织,全部外包给 Agent 本身。这个分工比「什么都自己带一套 LLM」的方案便宜一个数量级,5 个结构性查询,逐文件探索烧 41.2 万 token,图查询只要 3400,降幅 99.2%。数字来自他们挂 arXiv 的论文(2603.27277),31 个真实仓库实测,答案质量 83%,工具调用少 2.1 倍。自测数据,参考着看,但量级差距摆在那。

什么场景该用它。代码库大到 Agent 每次探索都在烧钱,或者团队多人协作同一个库,值得把索引做成共享资产,那个 graph.db.zst 制品就是为这准备的。什么场景别碰。仓库只有几十个文件,grep 就够,上这套属于高射炮打蚊子。超大单体仓库加内存紧张的机器,先等等,OOM 那条 issue 关了再说。

我一直觉得,Agent 基建这道题里最容易被漏掉的一问,是哪些能力可以直接省掉。codebase-memory 省掉了内置 LLM,省掉了语言服务器进程,连版本检查的网络请求都省了,省下来的钱最后体现在 token 账单上。纯 C 只是这套省法顺手抄起来的工具。

评论互动

© 2026 王若风的技术博客 · Powered by Astro