codegraph-mcp
# codegraph-mcp
An [MCP](https://modelcontextprotocol.io) server that indexes a TypeScript/JavaScript
codebase into a real call graph and import graph — using TypeScript's own
type checker for symbol resolution, not regex or text matching — and exposes
it to Claude (or any MCP client) as queryable tools.
Ask Claude things like:
- *"What would break if I change `queue.ts`?"*
- *"Who calls `createUser`?"*
- *"Show me the dependency graph of this module."*
Instead of Claude re-reading your whole codebase to answer, it gets a precise,
structural answer from a pre-built graph.
No paid APIs, no paid services. Free npm registry / GitHub used only for
testing against real-world code (see below).
## Why this exists
Code-intelligence tools that build a persistent, queryable graph of a
codebase are one of the fastest-moving categories in AI developer tooling
right now — this is a scoped-down, honestly-documented version of that idea,
built to demonstrate the underlying technique rather than compete with the
production tools in that space (see Limitations below).
## How it actually works (the part that matters)
Naive versions of this idea use regex or text matching to find function
calls — which breaks constantly (a call inside a comment, a variable that
happens to share a name with a function, an imported name that shadows a
local one). This project instead uses the **real TypeScript compiler API**:
it builds an actual `ts.Program`, asks the **type checker** to resolve every
call expression to its true declaration, and only records an edge when that
resolution succeeds.
This caught a real, non-obvious bug during development: when an identifier
comes from an `import { x } from "..."`, the checker initially returns an
**alias symbol** pointing at the import binding itself, not the original
declaration — resolving through that alias (`checker.getAliasedSymbol`) is
what makes cross-file call resolution actually work. Without that step, only
same-file calls would ever be found. See `src/parser.ts` for the fix.
## Verified output
**On a hand-written 3-file fixture** (`test/fixtures/sample-project`) — full
automated test suite, 20/20 passing, including cross-file resolution:
```
--- Call edges (the hard part: cross-file resolution via the type checker) ---
PASS: handleSignup calls createUser (CROSS-FILE — requires alias resolution)
PASS: main calls handleSignup (CROSS-FILE — requires alias resolution)
PASS: createUser calls hashPassword (same-file)
PASS: Exactly 3 call edges (no false positives)
--- Query engine ---
PASS: findCallers(createUser) returns exactly handleSignup
PASS: impactAnalysis(userService.ts) finds index.ts at depth 2 (TRANSITIVE, not direct)
20 passed, 0 failed
```
**On a real, published open-source library** ([sindresorhus/p-queue](https://github.com/sindresorhus/p-queue),
a well-known ~2,000-star npm package) — indexed live via the actual MCP tool
call, not a script:
```
Indexed 5 file(s): 32 symbol(s), 7 call edge(s), 7 import edge(s).
```
with a correctly resolved summary — `lowerBound` (a binary-search helper)
shown being called from within `PriorityQueue.enqueue`, and `queue.ts`
correctly identified as the most depended-on file (3 dependents) — matching
p-queue's actual architecture. This also caught a second real bug: p-queue's
source uses ESM-style imports with explicit `.js` extensions
(`import x from './priority-queue.js'`) even though the real file on disk is
`.ts` — extremely common in modern TypeScript/NodeNext projects. The
resolver now handles that extension swap; see `src/parser.ts`.
## Tools
| Tool | Description |
|---|---|
| `index_codebase` | Parse a codebase into a graph. Run this first. |
| `find_definition` | Find where a symbol is defined, by name |
| `find_callers` | Who calls a given symbol (cross-file aware) |
| `find_callees` | What a given symbol calls |
| `get_file_dependencies` | Direct import relationships for a file |
| `impact_analysis` | Transitive closure of files affected by changing a file |
| `get_graph_summary` | Stats: most-called symbols, most-depended-on files |
## Setup
```bash
git clone https://github.com/SiwarKhalfaoui/codegraph-mcp.git
cd codegraph-mcp
npm install
npm run build
```
### As a standalone CLI (no Claude needed)
```bash
node build/cli.js index /path/to/some/project/src
node build/cli.js summary
node build/cli.js find-definition createUser
node build/cli.js impact src/queue.ts
node build/cli.js export-mermaid calls --out call-graph.mmd
```
### Connect it to Claude Desktop
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`
on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
```json
{
"mcpServers": {
"codegraph": {
"command": "node",
"args": ["/absolute/path/to/codegraph-mcp/build/mcpServer.js"]
}
}
}
```
Or test directly without Claude Desktop using [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
```bash
npx @modelcontextprotocol/inspector node build/mcpServer.js
```
## Development
```bash
npm run dev # run the CLI directly with tsx, no build step
npm test # full automated test suite against the fixture project
```
## Architecture notes
- `src/fileDiscovery.ts` — walks the filesystem for source files, skipping
`node_modules`/build output/tests, with cross-platform path normalization
- `src/parser.ts` — the core: builds a `ts.Program`, extracts every
function/method/class declaration, then resolves every call expression
and import specifier via the real type checker
- `src/graphStore.ts` — persists the graph as JSON, not SQLite. **Deliberate
tradeoff**: a native SQLite binding needs platform-specific compilation
(`node-gyp`), which is exactly the kind of cross-platform friction this
project avoids. A repo-sized graph fits comfortably in memory as JSON —
SQLite would be the right upgrade for very large monorepos, not for this
scope.
- `src/queries.ts` — builds in-memory indices (by id, by name, adjacency
lists) over the loaded graph and answers the actual questions
- `src/mermaid.ts` — visual export for human inspection
- `src/mcpServer.ts` — wraps the query engine as MCP tools
- `src/cli.ts` — same functionality as a standalone command-line tool
## Known limitations (stated honestly)
- **Single-language scope.** TypeScript/JavaScript only — not the 158
languages some production code-intelligence tools support. This is a
focused demonstration of the technique, not a general-purpose tool.
- **Private class members (`#method`) aren't tracked yet.** TypeScript's
private-field syntax uses a different AST node type than regular method
names; this project doesn't yet register or resolve calls to them.
- **Static analysis only.** Dynamic dispatch (calling a function stored in
a variable that could point at several different implementations) isn't
resolved — only calls the type checker can statically determine.
- **JSON storage, not a real database.** Fine at single-repo scale;
wouldn't scale cleanly to a huge monorepo (see architecture notes above).
## Possible extensions
- Support private class members (`#foo`) as trackable symbols
- Incremental re-indexing (only re-parse changed files) instead of a full
rebuild every time
- A small web UI rendering the Mermaid export interactively instead of
static text
## License
MIT
TDQS
Scored across 7 tools
Each tool addresses a distinct aspect of code graph analysis: initialization, definition lookup, forward/reverse call relationships, direct file dependencies, transitive impact, and summary statistics. There is no overlap between find_callers, find_callees, and impact_analysis, as each operates at a different level of granularity or direction.
Most tools follow a clear verb_noun pattern (index_codebase, find_definition, find_callers, find_callees, get_file_dependencies, get_graph_summary), but impact_analysis breaks the convention as a compound noun rather than an imperative verb phrase. The consistency is strong overall.
Seven tools is a well-scoped count for a code graph server. Each tool covers a necessary capability without redundancy, making the set feel complete and manageable.
The tool surface covers the full lifecycle of code graph exploration: building the graph, finding definitions, traversing callers/callees, examining file dependencies, performing transitive impact analysis, and obtaining summary stats. There are no obvious dead ends or missing core operations.