ast-lens-mcp 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +169 -3
- package/dist/index.js +3007 -202
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 - 2026-05-29
|
|
4
|
+
|
|
5
|
+
This release expands `ast-lens-mcp` from eight tools to twelve.
|
|
6
|
+
|
|
7
|
+
New tools:
|
|
8
|
+
|
|
9
|
+
- `import_graph` maps resolved import, re-export, dynamic import, and `require()` edges.
|
|
10
|
+
- `detect_circular_deps` reports circular dependencies in the module graph.
|
|
11
|
+
- `find_dead_files` finds source files that are not reachable from package or index entry points.
|
|
12
|
+
- `api_surface` shows the public symbols reachable from a package or entry file, with best-effort signatures.
|
|
13
|
+
|
|
14
|
+
Also fixed `find_unused_exports` for modern TS-ESM projects that write relative imports or re-exports with `.js` specifiers while the source files are `.ts`, `.tsx`, `.mts`, `.cts`, or `.d.ts`. Those public re-exports are now resolved back to source instead of being misread as unused.
|
|
15
|
+
|
|
16
|
+
Verification for this release: `npm run typecheck`, `npm test` (178 tests), `npm run build`, and `npm run smoke` (12 tools exposed and exercised).
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> An MCP server that gives AI agents **structural understanding** of a TypeScript / JavaScript codebase — so an agent can *query* code structure instead of reading whole files into its context window.
|
|
4
4
|
|
|
5
|
-
`ast-lens-mcp` parses your project with the Babel AST toolchain and exposes
|
|
5
|
+
`ast-lens-mcp` parses your project with the Babel AST toolchain and exposes twelve focused, read-only tools over the [Model Context Protocol](https://modelcontextprotocol.io). Point it at a project root and an LLM client (Claude Desktop, Cursor, or anything that speaks MCP) can ask precise structural questions: *what symbols are exported here? where is this function called? which functions are too complex? what does this module import? what exports or files look dead? what does this package expose?* — all without paging entire files through the model.
|
|
6
6
|
|
|
7
7
|
It runs entirely on your **local files**. No API keys, no network calls, no credentials.
|
|
8
8
|
|
|
@@ -16,7 +16,7 @@ It runs entirely on your **local files**. No API keys, no network calls, no cred
|
|
|
16
16
|
|
|
17
17
|
Coding agents waste a lot of context re-reading files just to answer structural questions ("does this symbol exist?", "where is it used?", "what's the shape of this class?"). Those questions have *exact* answers that come from the AST, not from an approximate read. `ast-lens-mcp` turns them into cheap, deterministic tool calls that return small structured JSON — leaving more of the model's context for actual reasoning.
|
|
18
18
|
|
|
19
|
-
It is deliberately **syntactic, not type-aware**: it parses, it does not type-check. That keeps it fast, dependency-light, and able to analyze a
|
|
19
|
+
It is deliberately **syntactic, not type-aware**: it parses, it does not type-check. That keeps it fast, dependency-light, and able to analyze files without a whole-program build. Some graph tools read simple `tsconfig` path aliases for better module resolution, but name/reference tools stay name-based. `find_references` is therefore a precise *name-based* search classified by syntactic role, not a type-resolved rename index — see the note on that tool below.
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -102,6 +102,21 @@ All paths passed to tools are interpreted **relative to the project root** (or a
|
|
|
102
102
|
|
|
103
103
|
Every tool is read-only (`readOnlyHint: true`, `openWorldHint: false`), validates input with a strict zod schema, returns both human-readable text and machine-readable `structuredContent`, supports `response_format: "json" | "markdown"`, and never throws on bad input — parse failures come back as structured `parseErrors`.
|
|
104
104
|
|
|
105
|
+
| # | Tool | Use it for |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| 1 | `list_symbols` | Top-level and exported symbols across files, folders, or globs |
|
|
108
|
+
| 2 | `get_file_outline` | One-file class/interface/enum structure |
|
|
109
|
+
| 3 | `find_references` | Name-based identifier references with syntactic context |
|
|
110
|
+
| 4 | `search_ast` | Structural AST queries and common code smells |
|
|
111
|
+
| 5 | `analyze_complexity` | Per-function cyclomatic complexity and LOC |
|
|
112
|
+
| 6 | `summarize_module` | A file's imports, exports, and dependencies |
|
|
113
|
+
| 7 | `find_unused_exports` | Exported symbols that appear unused outside their defining file |
|
|
114
|
+
| 8 | `call_graph` | Function-to-function call relationships |
|
|
115
|
+
| 9 | `import_graph` | Resolved module import, re-export, and dynamic-import edges |
|
|
116
|
+
| 10 | `detect_circular_deps` | Circular dependencies in the module graph |
|
|
117
|
+
| 11 | `find_dead_files` | Source files not reachable from package or index entry points |
|
|
118
|
+
| 12 | `api_surface` | Public symbols reachable from a package or entry file |
|
|
119
|
+
|
|
105
120
|
### 1. `list_symbols`
|
|
106
121
|
|
|
107
122
|
All top-level / exported symbols (function, class, interface, type, enum, const/let/var) across a file, directory, or glob.
|
|
@@ -276,6 +291,151 @@ One file's imports, exports, and dependencies — including static imports, re-e
|
|
|
276
291
|
}
|
|
277
292
|
```
|
|
278
293
|
|
|
294
|
+
### 7. `find_unused_exports`
|
|
295
|
+
|
|
296
|
+
Candidate dead public surface: exported symbols that are never referenced from another file in the scanned scope. Package entry points and index barrels are treated as intentional public API by default.
|
|
297
|
+
|
|
298
|
+
```jsonc
|
|
299
|
+
// input
|
|
300
|
+
{ "target": "src", "entryPoints": ["src/index.ts"] }
|
|
301
|
+
|
|
302
|
+
// output (abridged)
|
|
303
|
+
{
|
|
304
|
+
"scanned": 27,
|
|
305
|
+
"totalExports": 71,
|
|
306
|
+
"total": 4,
|
|
307
|
+
"unused": [
|
|
308
|
+
{ "file": "src/internal.ts", "name": "debugOnly", "exportKind": "named", "reexport": false }
|
|
309
|
+
],
|
|
310
|
+
"entryPoints": ["src/index.ts"],
|
|
311
|
+
"parseErrors": []
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
This is name-based, not type-resolved. It is meant to produce a review list, not an automatic delete list.
|
|
316
|
+
|
|
317
|
+
### 8. `call_graph`
|
|
318
|
+
|
|
319
|
+
Function-to-function call relationships for a file, folder, or glob. Nodes are function-like definitions; edges are call sites from the enclosing function to the resolved callee when there is a clear name-based match.
|
|
320
|
+
|
|
321
|
+
```jsonc
|
|
322
|
+
// input
|
|
323
|
+
{ "target": "src/core", "includeExternalCalls": false }
|
|
324
|
+
|
|
325
|
+
// output (abridged)
|
|
326
|
+
{
|
|
327
|
+
"nodeCount": 42,
|
|
328
|
+
"edgeCount": 67,
|
|
329
|
+
"unresolvedCallees": 12,
|
|
330
|
+
"nodes": [
|
|
331
|
+
{ "id": "src/core/parser.ts#parseFile@28", "name": "parseFile", "file": "src/core/parser.ts", "kind": "function" }
|
|
332
|
+
],
|
|
333
|
+
"edges": [
|
|
334
|
+
{ "from": "src/core/parser.ts#parseFile@28", "callee": "parse", "to": "src/core/parser.ts#parse@11", "file": "src/core/parser.ts" }
|
|
335
|
+
],
|
|
336
|
+
"parseErrors": []
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Set `includeExternalCalls: true` when you want calls to libraries, built-ins, or unresolved methods kept in the edge list with `to: null`.
|
|
341
|
+
|
|
342
|
+
### 9. `import_graph`
|
|
343
|
+
|
|
344
|
+
Resolved module-level imports, re-exports, dynamic imports, and `require()` calls. Relative paths, directory indexes, TS-ESM `.js` specifiers, and simple `tsconfig` path aliases are mapped to concrete in-scope files when possible.
|
|
345
|
+
|
|
346
|
+
```jsonc
|
|
347
|
+
// input
|
|
348
|
+
{ "target": "src", "includeExternal": true }
|
|
349
|
+
|
|
350
|
+
// output (abridged)
|
|
351
|
+
{
|
|
352
|
+
"nodeCount": 27,
|
|
353
|
+
"edgeCount": 97,
|
|
354
|
+
"internalEdges": 97,
|
|
355
|
+
"externalCount": 11,
|
|
356
|
+
"unresolvedEdges": 0,
|
|
357
|
+
"nodes": [{ "id": "src/index.ts", "entry": true }],
|
|
358
|
+
"edges": [
|
|
359
|
+
{ "from": "src/server.ts", "specifier": "./tools/listSymbols.js", "to": "src/tools/listSymbols.ts", "kind": "import", "resolution": "internal", "symbols": ["registerListSymbols"] }
|
|
360
|
+
],
|
|
361
|
+
"parseErrors": []
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Scope matters: if the imported file is outside `target`, the edge is reported as unresolved.
|
|
366
|
+
|
|
367
|
+
### 10. `detect_circular_deps`
|
|
368
|
+
|
|
369
|
+
Circular import dependencies in the module graph, including self-imports. It is path-based and works from the same resolved graph model as `import_graph`.
|
|
370
|
+
|
|
371
|
+
```jsonc
|
|
372
|
+
// input
|
|
373
|
+
{ "target": "src", "includeDynamic": true, "includeTypeOnly": true }
|
|
374
|
+
|
|
375
|
+
// output (abridged)
|
|
376
|
+
{
|
|
377
|
+
"scanned": 27,
|
|
378
|
+
"edgeCount": 97,
|
|
379
|
+
"cycleCount": 0,
|
|
380
|
+
"hasCycles": false,
|
|
381
|
+
"cycles": [],
|
|
382
|
+
"unresolvedLocal": [],
|
|
383
|
+
"parseErrors": []
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Use `includeTypeOnly: false` when type-only cycles are noise for the question you are asking.
|
|
388
|
+
|
|
389
|
+
### 11. `find_dead_files`
|
|
390
|
+
|
|
391
|
+
Candidate orphan modules: source files that are not reachable from package.json entries or index-style entry points. Results include why a file is suspicious and which unreachable files still import it.
|
|
392
|
+
|
|
393
|
+
```jsonc
|
|
394
|
+
// input
|
|
395
|
+
{ "target": "src", "entryPoints": ["src/index.ts"] }
|
|
396
|
+
|
|
397
|
+
// output (abridged)
|
|
398
|
+
{
|
|
399
|
+
"scanned": 27,
|
|
400
|
+
"entryFileCount": 1,
|
|
401
|
+
"reachableCount": 27,
|
|
402
|
+
"total": 0,
|
|
403
|
+
"deadFiles": [],
|
|
404
|
+
"entryPoints": ["src/index.ts"],
|
|
405
|
+
"parseErrors": []
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Dynamic imports, test-only entry points, generated files, and assets can all change the interpretation, so treat the result as a triage list.
|
|
410
|
+
|
|
411
|
+
### 12. `api_surface`
|
|
412
|
+
|
|
413
|
+
The public API reachable from a package directory or a single entry file. It follows re-exports transitively and returns each public symbol with a best-effort source signature.
|
|
414
|
+
|
|
415
|
+
```jsonc
|
|
416
|
+
// input
|
|
417
|
+
{ "target": "src/server.ts", "includeMembers": true }
|
|
418
|
+
|
|
419
|
+
// output (abridged)
|
|
420
|
+
{
|
|
421
|
+
"target": "src/server.ts",
|
|
422
|
+
"packageEntryPoints": [],
|
|
423
|
+
"entries": [
|
|
424
|
+
{
|
|
425
|
+
"entry": "src/server.ts",
|
|
426
|
+
"symbols": [
|
|
427
|
+
{ "name": "createServer", "kind": "function", "exportKind": "named", "signature": "function createServer(opts: CreateServerOptions): McpServer", "declaredIn": "src/server.ts" }
|
|
428
|
+
],
|
|
429
|
+
"unresolved": []
|
|
430
|
+
}
|
|
431
|
+
],
|
|
432
|
+
"totalSymbols": 1,
|
|
433
|
+
"parseErrors": []
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Pass `target: "."` for a package root, or an explicit file such as `src/server.ts` when you want one entry file only.
|
|
438
|
+
|
|
279
439
|
---
|
|
280
440
|
|
|
281
441
|
## Architecture
|
|
@@ -289,12 +449,18 @@ src/
|
|
|
289
449
|
files.ts # glob/dir/file discovery, node_modules ignore, path-traversal sandbox
|
|
290
450
|
traverse.ts # shared traversal helpers + cyclomatic-complexity engine
|
|
291
451
|
extract.ts # symbol / outline / module-summary extraction
|
|
452
|
+
entryPoints.ts # package entry and TS-ESM source-resolution helpers
|
|
453
|
+
importGraph.ts # resolved import/re-export graph builder
|
|
454
|
+
moduleGraph.ts # dependency-cycle graph helpers
|
|
455
|
+
signature.ts # best-effort source signature extraction
|
|
292
456
|
context.ts # ServerContext: shared root + cache + batch loader
|
|
293
457
|
response.ts # JSON/Markdown formatting, character-limit guard, error results
|
|
294
458
|
types.ts # shared structured-output types
|
|
295
459
|
tools/ # one file per tool, each exporting register<Tool>(server, ctx)
|
|
296
460
|
listSymbols.ts getFileOutline.ts findReferences.ts
|
|
297
461
|
searchAst.ts analyzeComplexity.ts summarizeModule.ts
|
|
462
|
+
findUnusedExports.ts callGraph.ts importGraph.ts
|
|
463
|
+
detectCircularDeps.ts findDeadFiles.ts apiSurface.ts
|
|
298
464
|
shared.ts # shared zod schema fragments
|
|
299
465
|
test/
|
|
300
466
|
core.test.ts # parser, cache, path-safety, complexity
|
|
@@ -318,7 +484,7 @@ Design notes:
|
|
|
318
484
|
npm install
|
|
319
485
|
npm run build # bundle with tsup -> dist/index.js (executable, ESM)
|
|
320
486
|
npm run typecheck # tsc --noEmit, strict, over src + tests
|
|
321
|
-
npm test # vitest run (
|
|
487
|
+
npm test # vitest run (178 tests)
|
|
322
488
|
npm run smoke # build first, then boot the server over stdio and call tools
|
|
323
489
|
npm run dev # tsx watch (run the server from source)
|
|
324
490
|
```
|