给 AI Agent 装上代码大脑:CodeGraph
给两个项目配了 CodeGraph MCP,跑 /mcp 一看:ENOENT。连不上。Node 路径没问题,二进制文件存在,手动启动正常。折腾了半天才定位到原因——但这是后话。先说说这个工具本身值得折腾的地方。
tree-sitter → SQLite 知识图谱
CodeGraph 的核心思路很直接:用 tree-sitter 把源码解析成 AST,然后把每个函数、类、调用关系提取出来,存进 SQLite。
不是跑 LLM 做总结。是确定性的结构提取。同样的代码,跑十次得到一样的结果。
一条边代表一个关系——calls(调用了谁)、extends(继承了谁)、imports(引入了什么)。一个节点代表一个符号——函数、方法、类、接口。25 种语言,20 种节点类型,12 种边类型。
我的一个 Go 微服务项目,5800 个文件,解析完得到 245000 个节点和 532000 条边。数据库 400MB。查询耗时毫秒级。
五层管线
整个处理流程分五层,每一层只做一件事。
提取层。ExtractionOrchestrator 用 tree-sitter 的 WASM 运行时解析 AST。每种语言有一个 LanguageExtractor,定义了哪些 AST 节点是函数、哪些是类。解析在 Worker 线程里跑,每 250 个文件回收一次 WASM 堆内存。节点 ID 是 SHA256(文件路径:类型:名称:行号) 截断到 32 位 hex。
数据库层。SQLite 配置为 WAL 模式 + 64MB 页缓存。节点表存符号信息,边表存关系,FTS5 虚拟表做全文搜索。搜索走三步策略:FTS5 前缀匹配 → LIKE 子串 → Levenshtein 模糊匹配,综合 BM25 + 类型权重 + 路径相关性评分。
解析层。这是最复杂的部分。提取阶段产生的"未解析引用"在这一层连接到实际目标。按置信度从高到低试三种策略:框架识别(15+ 框架的约定)→ import 路径解析(含 barrel 文件 re-export 链追踪,深度 8)→ 名称匹配(精确名 → 限定名 → 方法调用 → 模糊匹配)。边创建时还会做类型提升,比如 extends 在目标是接口时变成 implements。
图遍历层。BFS/DFS 遍历,可配置深度、边类型、方向。BFS 优先级是 contains > calls > 其他——结构上下文优先于调用关系。提供 getCallers、getCallees、getImpactRadius、findCircularDependencies、findDeadCode 等高级查询。
上下文层。面向 AI 消费的核心层。从自然语言查询提取符号,多通道搜索(精确 → 词干 → FTS5 → CamelCase 边界匹配),类型层次扩展,多样性控制(单文件上限 20%,测试文件上限 15%)。输出为 Markdown 或 JSON。
串起来就是:你问「谁调用了 HandleLogin」,CodeGraph 在毫秒内从 532000 条边里找到答案,把结果格式化成 AI agent 能直接理解的上下文。
和 grep 有什么不同
grep 搜的是文本。CodeGraph 查的是结构。
「HandleLogin 在哪定义的?」——grep 能搜到文件和行号,但不知道它是个方法还是函数,不知道它的参数类型,不知道谁调用了它。CodeGraph 返回的是:这是一个 method,属于 AuthHandler 类,签名是 (ctx *gin.Context),有 7 个 caller,其中 3 个在测试文件里。
「改了这个 struct 会影响什么?」——grep 无能为力。CodeGraph 的 impact radius 沿着 calls、references、contains 边做 BFS 遍历,告诉你改动会波及哪些模块。
「这个函数的调用链是什么?」——从入口到目标,最短路径。不是正则匹配,是图的 BFS。
这也是为什么它给 AI agent 带来的价值不是「更快地搜索」,而是「理解代码结构」。agent 知道改一个函数会影响谁,不用 guess。
MCP 配置的两层陷阱
说回开头那个 ENOENT。
排查过程绕了弯路。先是怀疑 enabledMcpjsonServers 白名单没加 codegraph——确实没加,加完还是不行。然后怀疑 ~/.claude/mcp.json 的格式不对,试了加 mcpServers 包裹层、去掉包裹层——都不对。
真正的问题藏在 ~/.claude.json 里。
Claude Code 有两套 MCP 配置机制。~/.claude/mcp.json 是手动管理的,扁平格式 { "serverName": { "command": "..." } }。~/.claude.json 里有个 mcpServers 字段,是 codegraph install 自动写入的,格式是 { "mcpServers": { "serverName": { ... } } }。
安装器写入的那条配置长这样:
{
"mcpServers": {
"codegraph": {
"type": "stdio",
"command": "codegraph",
"args": ["serve", "--mcp"]
}
}
}command 是裸命令名 "codegraph"。但我的系统上 codegraph 不在全局 PATH 里——npm list -g 是空的。Claude Code spawn 子进程时找不到这个可执行文件,于是 ENOENT。
我一直在改 ~/.claude/mcp.json,但 Claude Code 优先读的是 ~/.claude.json。改错了文件。
修复方式是把 command 改成绝对路径:
{
"mcpServers": {
"codegraph": {
"type": "stdio",
"command": "/opt/homebrew/opt/node@22/bin/node",
"args": [
"/Users/mrpwn/default/Project/codegraph/dist/bin/codegraph.js",
"serve",
"--mcp"
]
}
}
}ENOENT 这个报错误导性很强。它说的是「找不到文件」,但不是你代码里的文件找不到,是 MCP server 的可执行文件找不到。排查方向很容易跑偏。
知识图谱给 AI agent 装上了「导航」。但给这个导航接上电源的过程提醒我:工具链的基础设施还没到无感的程度。配置文件有两套,格式不一样,优先级不明确。ENOENT 告诉你「文件不存在」,但没告诉你是哪个文件、哪个配置源。以后遇到 MCP 连不上,第一件事应该是 cat ~/.claude.json | grep mcpServers。
← All posts