为 Claude Code、Cursor、Codex CLI、opencode 提供预索引语义代码知识图谱的 MCP 工具。Agent 探索代码库时,从扫描文件(grep/glob/Read)变为查询图谱,平均减少 92% 工具调用,提速 71%。
基本信息
- 仓库:https://github.com/colbymchenry/codegraph
- npm:
@colbymchenry/codegraph(v0.7.8) - 本地路径:
/Users/zhaoweiguo/6ai/opensources/codegraph - 技术栈:Node.js + tree-sitter(AST 解析)+ SQLite(FTS5)+ MCP 协议
- 许可证:MIT
安装与初始化
# 交互式安装(自动检测并配置 Claude Code/Cursor/Codex/opencode)
npx @colbymchenry/codegraph
# 初始化项目(构建知识图谱索引)
cd your-project
codegraph init -i安装器会自动写入 ~/.claude/CLAUDE.md、MCP 配置、权限 allow 列表,无需手动配置。
核心原理
Agent 探索请求
│
▼
codegraph_explore(1次调用)
│
▼
SQLite 图谱(符号节点 + 调用边)
│
▼
返回:入口点 + 相关符号 + 代码片段
- 提取:tree-sitter 解析 AST,提取函数/类/方法节点和调用/导入/继承边
- 存储:本地 SQLite(
.codegraph/codegraph.db)+ FTS5 全文搜索 - 解析:函数调用→定义、导入→源文件、类继承、框架路由
- 自动同步:原生 OS 文件事件(FSEvents/inotify)+ 2 秒防抖增量更新
基准测试
| 代码库 | 有 CodeGraph | 无 CodeGraph | 提升 |
|---|---|---|---|
| VS Code(TypeScript) | 3 次调用,17s | 52 次调用,1m37s | 94% 少·82% 快 |
| Excalidraw(TypeScript) | 3 次调用,29s | 47 次调用,1m45s | 94% 少·72% 快 |
| Claude Code(Python+Rust) | 3 次调用,39s | 40 次调用,1m8s | 93% 少·43% 快 |
| Swift Compiler(25,874 文件) | 6 次调用,35s | 37 次调用,2m8s | 84% 少·73% 快 |
关键观察:有 CodeGraph 时,Agent 从不回退到读文件,完全信任图谱结果。
MCP 工具集
| 工具 | 用途 |
|---|---|
codegraph_explore | 主探索工具,一次返回入口点+相关符号+代码片段 |
codegraph_search | 按名称搜索符号(FTS5) |
codegraph_context | 为任务构建相关代码上下文 |
codegraph_callers | 查找调用某函数的所有位置 |
codegraph_callees | 查找某函数调用的所有函数 |
codegraph_impact | 修改某符号前分析影响范围 |
codegraph_node | 获取单个符号详情(可含源码) |
codegraph_files | 获取索引文件结构(比文件系统扫描快) |
codegraph_status | 查看索引健康状态和统计 |
使用规范(CLAUDE.md 中的约定)
- 主会话:只用轻量工具(
codegraph_search/callers/impact/node),用于编辑前的定向查找 - 探索任务:必须派生 Explore Agent,在 Agent 中使用
codegraph_explore - 原因:
codegraph_explore返回大量源码,会填满主会话上下文
支持语言(19+)
TypeScript、JavaScript、Python、Go、Rust、Java、C#、PHP、Ruby、C/C++、Swift、Kotlin、Scala、Dart、Svelte、Vue、Liquid、Pascal/Delphi
框架路由识别(13 个框架)
Django、Flask、FastAPI、Express、Laravel、Rails、Spring、Gin/chi/gorilla、Axum/actix/Rocket、ASP.NET、Vapor、React Router、SvelteKit
CLI 常用命令
codegraph init -i # 初始化并索引项目
codegraph index # 全量重建索引
codegraph sync # 增量更新
codegraph status # 查看统计(节点数、边数、后端类型)
codegraph query <name> # 搜索符号
codegraph context <task> # 为任务构建上下文
codegraph affected <files> # 找出受变更影响的测试文件
codegraph serve --mcp # 启动 MCP 服务器codegraph affected(CI 集成)
# 只跑受影响的测试
git diff --name-only HEAD | codegraph affected --stdin --quiet | xargs npx vitest run配置(.codegraph/config.json)
{
"languages": ["typescript", "javascript"],
"exclude": ["node_modules/**", "dist/**"],
"maxFileSize": 1048576,
"extractDocstrings": true,
"trackCallSites": true
}注意事项
- 默认使用
better-sqlite3(原生,快);不可用时降级为 WASM SQLite(慢 5-10x,可能出现database is locked) - 运行
codegraph status查看Backend: native还是Backend: wasm - 100% 本地,无数据外传,无需 API Key
实操示例:基础三步(以 codegraph 自身为例)
示例项目:
/Users/zhaoweiguo/6ai/opensources/codegraph(TypeScript,113 文件)
Step 1:init — 初始化项目
cd /path/to/your-project
codegraph init输出:
┌ Initializing CodeGraph
│
◆ Initialized in /path/to/your-project
│
● Run "codegraph index" to index the project
│
└ Done
init 做了什么:
- 创建
.codegraph/目录 - 生成
.codegraph/codegraph.db(空 SQLite 数据库) - 生成
.codegraph/config.json(自动检测语言,配置 include/exclude 规则)
生成的 config.json 关键字段:
{
"version": 1,
"include": ["**/*.ts", "**/*.js", "**/*.py", "**/*.go", "..."],
"exclude": ["**/node_modules/**", "**/dist/**", "**/build/**", "..."],
"languages": [],
"maxFileSize": 1048576,
"extractDocstrings": true,
"trackCallSites": true
}languages: [] 表示自动检测,无需手动指定。
Step 2:index — 构建知识图谱
codegraph index输出:
┌ Indexing project
│
◆ Scanning files — 113 found
◆ Parsing code — done
◆ Resolving refs — done
│
◆ Indexed 113 files
│
● 1,650 nodes, 1,537 edges in 1.1s
│
└ Done
index 的四个阶段:
- scanning — 按 include/exclude 规则扫描文件
- parsing — tree-sitter 解析 AST,提取节点(函数/类/方法/接口)和边(调用/导入/继承)
- storing — 写入 SQLite,建 FTS5 全文索引
- resolving — 解析跨文件引用:函数调用→定义、import→源文件、类继承
完成后用 codegraph status 验证:
codegraph statusCodeGraph Status
Project: /path/to/your-project
Index Statistics:
Files: 113
Nodes: 1,650
Edges: 4,292
DB Size: 3.91 MB
Backend: native ← 原生 SQLite,性能最佳
Nodes by Kind:
method 498
import 427
function 286
constant 191
file 113
interface 76
class 37
type_alias 15
variable 7
Files by Language:
typescript 108
javascript 5
✓ Index is up to date
Edges: 4,292比 index 时显示的1,537 edges多,因为 status 统计的是解析后加上 import 边的总数。
Step 3:query — 搜索符号
3a. 按名称搜索符号
codegraph query "searchNodes"Search Results for "searchNodes":
method searchNodes (10161%)
src/index.ts:653
(query: string, options?: SearchOptions): SearchResult[]
method searchNodes (10050%)
src/db/queries.ts:481
(query: string, options: SearchOptions = {}): SearchResult[]
method searchNodesFTS (5558%)
src/db/queries.ts:695
method searchNodesLike (5316%)
src/db/queries.ts:758
method searchNodesFuzzy (5107%)
src/db/queries.ts:636
括号里的百分比是 FTS5 相关性得分,越高越匹配。结果直接给出文件路径和行号,以及函数签名。
3b. 为任务构建上下文
codegraph context "how does indexing work"## Code Context
**Query:** how does indexing work
### Entry Points
- IndexResult (interface) - src/extraction/index.ts:65
- IndexOptions (interface) - src/index.ts:112
- IndexProgress (interface) - src/extraction/index.ts:55
### Related Symbols
- src/extraction/index.ts: indexAll:484, indexFiles:979, sync:1203
- src/index.ts: indexAll:375, indexFiles:417, sync:437
### Code
(直接返回相关符号的完整源码片段)
context 命令比 query 更智能:它理解自然语言任务描述,自动找出入口点和相关符号,并返回源码——这正是 MCP 工具 codegraph_context 的底层实现。
query 常用选项
codegraph query "UserService" # 搜索符号名
codegraph query "login" --limit 10 # 限制结果数
codegraph query "handler" --json # JSON 格式输出
codegraph context "fix login bug" # 自然语言任务 → 相关代码上下文内部原理深解
init 内部做了什么
三件事:
-
initGrammars()— 加载 tree-sitter 的 WASM 语法文件(每种语言一个.wasm),全局初始化一次,后续 parse 才能用。 -
创建
.codegraph/目录 +config.json— include/exclude 规则是硬编码默认值,覆盖 19 种语言扩展名和常见构建产物目录(node_modules/dist/target 等)。languages: []表示自动检测,无需手动指定。 -
DatabaseConnection.initialize()— 创建 SQLite 数据库,执行schema.sql,建好所有表和索引:
| 表 | 作用 |
|---|---|
nodes | 存符号节点:id、kind、name、file_path、start_line、end_line、signature、docstring |
edges | 存关系:source → target + kind(calls/imports/extends/implements) |
files | 记录已索引文件的 content_hash + modified_at,供增量 sync 判断是否变更 |
unresolved_refs | parsing 阶段发现的悬空引用(只知道调用了 foo,不知道定义在哪),等 resolving 处理 |
nodes_fts | FTS5 虚拟表,字段为 name/qualified_name/docstring/signature,通过 trigger 与 nodes 自动同步 |
init 完成后数据库是空的,只有 schema。
index 内部做了什么
四个阶段:
Phase 1: scanning
按 config 的 include/exclude glob 规则遍历文件系统,返回文件路径列表。同时做一次框架检测(扫描 urls.py/routes.rb/@app.route 等特征文件),确定项目用了哪些 web 框架,后续 parse 时框架特定的路由提取器才会激活。
Phase 2: parsing(主线程 + Worker 线程分离)
关键设计:解析工作发给独立的 parse-worker.js(Worker 线程),主线程只负责批量读文件(Promise.all 并发 I/O)和进度显示。原因:tree-sitter WASM 解析是 CPU 密集型,放 Worker 里不阻塞主线程的 UI 刷新。
Worker 对每个文件的处理流程:
- tree-sitter 把源码解析成 AST
- 用语言特定的 S-expression query 从 AST 提取节点和边
- 对特殊格式(Svelte/Vue/Liquid/DFM)用专门的 extractor 预处理
- 返回
{ nodes[], edges[] }
跨文件的函数调用此时是”未解析引用”——只知道调用了名叫 foo 的东西,不知道定义在哪。这些存入 unresolved_refs 表,等 Phase 4 处理。
Worker 容错机制:
- 单文件超时 → terminate + restart worker,该文件标记 error
- Worker crash(WASM OOM)→ reject 所有 pending,restart,继续剩余文件
- 最后兜底:strip 注释行后重试(针对大量
// CHECK:指令的编译器测试文件)
Phase 3: storing(隐含在 parsing 里)
每个文件 parse 完,主线程立即把 nodes/edges 写入 SQLite。写入 nodes 时,FTS5 的 trigger 自动把 name/signature/docstring 同步到 nodes_fts 虚拟表。
Phase 4: resolving
处理 unresolved_refs 表里的所有悬空引用,解析策略按优先级:
- import 路径解析:
foo是从哪个 import 语句引入的?找到 import 的源文件,再在那个文件里找foo的定义节点 - 名称匹配:在同语言的所有节点里按名称查找,支持 TypeScript path alias(
@/utils→src/utils) - 框架路由解析:把
@app.route('/login')这样的路由节点和对应的 handler 函数连起来 - 过滤内置符号:
console.log、useState、Python 的len()等内置符号直接跳过,不产生悬空引用
解析成功的引用转成正式的 edges 记录写回数据库。这就是为什么 status 显示的 edges 数(4292)比 index 时报告的(1537)多——resolving 之后加了大量跨文件调用边。
query 内部做了什么
searchNodes() 的三级降级搜索策略:
第一级:FTS5(主路径)
查询词发给 nodes_fts 虚拟表,BM25 算法打分,支持前缀匹配(search*)。
第二级:LIKE 子串匹配(FTS 无结果时)
WHERE name LIKE '%query%',捕获 FTS 漏掉的子串情况。
第三级:Levenshtein 模糊匹配(前两级都无结果时)
扫描所有节点名,计算编辑距离,容忍拼写错误(query 长度 ≥ 3 才触发)。
多信号重打分(三级搜索后统一执行)
最终得分 = BM25原始分
+ kindBonus(function/method/class > import/variable)
+ nameMatchBonus(精确匹配+30,前缀匹配+20)
+ scorePathRelevance(文件路径包含查询词加分)
按总分降序排列,截取 limit 条。
codegraph context 的额外步骤
在 searchNodes 基础上,沿 edges 做图遍历(BFS,默认 depth=2)扩展相关符号,最后把节点的源码片段一起返回。这是 MCP 工具 codegraph_context 的底层实现,也是 Agent 一次调用能拿到完整上下文的原因。