为 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 图谱(符号节点 + 调用边)
    │
    ▼
返回:入口点 + 相关符号 + 代码片段
  1. 提取:tree-sitter 解析 AST,提取函数/类/方法节点和调用/导入/继承边
  2. 存储:本地 SQLite(.codegraph/codegraph.db)+ FTS5 全文搜索
  3. 解析:函数调用→定义、导入→源文件、类继承、框架路由
  4. 自动同步:原生 OS 文件事件(FSEvents/inotify)+ 2 秒防抖增量更新

基准测试

代码库有 CodeGraph无 CodeGraph提升
VS Code(TypeScript)3 次调用,17s52 次调用,1m37s94% 少·82% 快
Excalidraw(TypeScript)3 次调用,29s47 次调用,1m45s94% 少·72% 快
Claude Code(Python+Rust)3 次调用,39s40 次调用,1m8s93% 少·43% 快
Swift Compiler(25,874 文件)6 次调用,35s37 次调用,2m8s84% 少·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 的四个阶段:

  1. scanning — 按 include/exclude 规则扫描文件
  2. parsing — tree-sitter 解析 AST,提取节点(函数/类/方法/接口)和边(调用/导入/继承)
  3. storing — 写入 SQLite,建 FTS5 全文索引
  4. resolving — 解析跨文件引用:函数调用→定义、import→源文件、类继承

完成后用 codegraph status 验证:

codegraph status
CodeGraph 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 内部做了什么

三件事:

  1. initGrammars() — 加载 tree-sitter 的 WASM 语法文件(每种语言一个 .wasm),全局初始化一次,后续 parse 才能用。

  2. 创建 .codegraph/ 目录 + config.json — include/exclude 规则是硬编码默认值,覆盖 19 种语言扩展名和常见构建产物目录(node_modules/dist/target 等)。languages: [] 表示自动检测,无需手动指定。

  3. 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_refsparsing 阶段发现的悬空引用(只知道调用了 foo,不知道定义在哪),等 resolving 处理
nodes_ftsFTS5 虚拟表,字段为 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 对每个文件的处理流程:

  1. tree-sitter 把源码解析成 AST
  2. 用语言特定的 S-expression query 从 AST 提取节点和边
  3. 对特殊格式(Svelte/Vue/Liquid/DFM)用专门的 extractor 预处理
  4. 返回 { 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 表里的所有悬空引用,解析策略按优先级:

  1. import 路径解析:foo 是从哪个 import 语句引入的?找到 import 的源文件,再在那个文件里找 foo 的定义节点
  2. 名称匹配:在同语言的所有节点里按名称查找,支持 TypeScript path alias(@/utils → src/utils)
  3. 框架路由解析:把 @app.route('/login') 这样的路由节点和对应的 handler 函数连起来
  4. 过滤内置符号: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 一次调用能拿到完整上下文的原因。