code-graph
Enables querying cross-repo code dependencies, HTTP routes, database tables, and queues via an MCP server using Cypher queries.
README
code-graph
A static connection graph of the Example platform. It scans app-web
(Laravel + React) and app-services (NestJS + Go + Python) and records how the
code is wired together — which files call which HTTP routes, which tables and
queues they read or write, and what they import — into an embedded
Kùzu graph database you can query from the terminal, from
Claude Code (MCP), or with raw Cypher.
It answers the cross-repo reachability questions that are otherwise a multi-file grep-and-guess exercise:
- Who actually calls
GET /resource-a? - What code touches the
table_btable? - How does the web frontend reach a backend service?
Quickstart
uv sync # install (Python 3.11–3.13)
uv run code-graph scan # build graph.kuzu from the sibling repos
uv run code-graph stats # node + per-edge-type counts
uv run code-graph query "callers:/resource-a" # who calls a route
By default scan looks for app-web and app-services as siblings of this
project (../app-web, ../app-services) and reads route data from
../route-inventory/dist/openapi.json. Point it at your own repos with env vars
instead of editing the code:
export CODE_GRAPH_REPOS="/path/to/web-repo,/path/to/services-repo" # or name=path pairs
export CODE_GRAPH_OPENAPI="/path/to/route-inventory/dist/openapi.json"
Missing repos are skipped, not errored. The scan is a static snapshot — re-run it after any change you want reflected. A full scan of two large repos takes roughly 15–20 minutes.
Each scan writes two gitignored artifacts at the project root: graph.kuzu
(the database) and parser-report.txt (scan counts).
The three interfaces
CLI
uv run code-graph scan
uv run code-graph stats
uv run code-graph query "<preset-or-cypher>"
uv run code-graph export --format html|graphml|dot
query takes one argument. If it looks like <preset>:<arg> it runs a preset;
otherwise it runs as raw Cypher.
| Preset | Finds |
|---|---|
callers:<route> |
files that issue an HTTP call to a matching route |
calls:<node> |
outbound edges from matching nodes |
touches:<table|queue> |
code that reads/writes a table or uses a queue |
neighbors:<node> |
one-hop neighbours of matching nodes |
search:<text> |
nodes whose id/name contains the text |
export writes graph.{html,graphml,dot} (all gitignored, regenerated on
demand). graph.html is a self-contained offline viewer; GraphML/DOT are for
external graph tools.
MCP (in Claude Code)
CODE_GRAPH_DB=/home/ohad/sh/projects/code-graph/graph.kuzu uv run code-graph-mcp
Exposes the query layer over stdio as seven read-only tools: find_callers,
what_calls, path_between, who_touches, neighbors, search_nodes,
run_cypher. The server refuses to start until graph.kuzu exists — run scan
first.
Raw Cypher
The CLI query command (when the argument isn't a preset) and the MCP
run_cypher tool run arbitrary Cypher against the model below. The database is
opened read-only at the Kùzu engine level, and run_cypher additionally
rejects write/DDL keywords before executing — the graph cannot be mutated
through either path.
uv run code-graph query "MATCH (f)-[c:HTTP_CALL]->(r) WHERE r.kind='route' RETURN f.id, r.id LIMIT 20"
Graph model
One Node table (discriminated by kind) and one relationship table per edge
type — the idiomatic Kùzu shape, so filtering is a kind predicate and every
traversal is a single relationship type.
Node kind |
Represents | Example id |
|---|---|---|
file |
a source file | file:app-web/app-frontend/src/components/callRecord/StatusCheck.jsx |
route |
an HTTP route (seeded from route-inventory) | route:service-b:GET:/system/health-check-full |
db_table |
a database table | db_table:table_b |
queue |
a Redis stream / channel | queue:service-c-stream |
package |
an imported package/module | package:@nestjs/common |
external |
an HTTP call that didn't resolve to a known route | external:https://example.com/x |
| Edge type | Meaning | confidence |
|---|---|---|
HTTP_CALL |
file → route it calls (or external if unresolved) |
0.8 resolved / 0.5 external |
DB_ACCESS |
file → table it reads/writes (Laravel $table/DB::table, Kysely) |
0.7 |
QUEUE_PUB / QUEUE_SUB |
file → queue/stream it publishes/consumes | 0.6 |
IMPORTS |
file → package or file it imports | 0.9 |
Every edge carries source_file, line, and raw (the matched snippet), plus
mode (read/write/unknown) on DB_ACCESS. confidence describes how the
match was made, not a statistical accuracy — treat low-confidence and
external edges as leads to verify against source_file:line, not ground truth.
How it works
- Parsing. tree-sitter plus targeted regex over PHP, TS/TSX, JS/JSX, Go, and Python. No type resolution or control-flow analysis, and deliberately recall-biased — it favours catching real edges over avoiding every false positive.
- Routes come from route-inventory. Route nodes aren't discovered by parsing;
they're loaded from
route-inventory's generateddist/openapi.json.HTTP_CALLedges resolve a call's(METHOD, path)against that catalog (with a suffix-match fallback and same-origin tie-break); unmatched calls becomeexternalnodes. - What's scanned. Both repos are walked recursively for the source
extensions above, excluding
node_modules,vendor,dist,build,.git,.claude(covers.claude/worktrees),.next,coverage. Symlinked directories are followed once, guarded by resolved real path.
Example output
From a scan of the real app-web + app-services trees (2026-07-16):
4248 files scanned → 10108 nodes, 0 parse errors
nodes: file 6653 · package 1594 · route 1559 · db_table 261 · external 41
edges: IMPORTS 21212 · DB_ACCESS 794 · HTTP_CALL 189 · QUEUE 0 (22195 total)
$ uv run code-graph query "callers:/system/health-check-full"
[
{
"caller": "file:app-web/app-frontend/src/components/callRecord/StatusCheck.jsx",
"route": "route:service-b:GET:/system/health-check-full",
"line": 168,
"confidence": 0.8
}
]
IMPORTS dwarfs the other edge types by design — one edge per import statement
across thousands of files, versus per-call-site matches elsewhere.
Known limitations
- Minified build bundles add noise.
app-web/public/js/*hashed Vite/webpack output is currently scanned, producing some spuriousHTTP_CALLedges and file nodes. Excluding build output at the walker level is a planned follow-up. - Relative imports aren't resolved. A relative specifier (
./x,../y) becomes a literalfile:node id rather than being resolved to the real file it points at, so a share offilenodes are synthetic import targets. Bare package imports resolve correctly. DB_ACCESS.modeisunknownfor PHP. Only the TS/Kysely path (selectFrom/insertInto/…) infers read vs. write; Laravel$table/DB::tablehits have no per-call-site signal, somodeis leftunknownrather than guessed.- Queues need string literals.
QUEUE_*edges are only created for string-literal stream/channel names; Kafka-stylesubscribe(variable)calls aren't named. (QUEUE = 0on the current tree is accurate, not a miss.)
Development
uv run pytest # 42 tests
src/code_graph/
cli.py CLI entry point (scan / stats / query / export)
mcp_server.py MCP entry point (code-graph-mcp)
config.py ScanConfig: sibling-repo paths, exclude list, db path
model.py Node/Edge dataclasses + stable id helpers
catalog.py RouteCatalog: loads route-inventory OpenAPI, resolves calls
walker.py source-file walker (language detection, excludes, symlink guard)
parsing.py tree-sitter parse cache + AST helpers
matchers/ HTTP_CALL / DB_ACCESS / QUEUE_PUB+SUB / IMPORTS extraction
graph/
loader.py Kùzu schema + CSV bulk loader
queries.py GraphDB: read-only presets + guarded raw-Cypher escape hatch
scan.py orchestrates walker → matchers → loader, writes parser-report.txt
report.py parser-report.txt writer
skill/SKILL.md Claude Code skill definition
docs/superpowers/ design spec + implementation plan
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。