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 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 six 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?* — all without paging entire files through the model.
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 file in isolation (no `tsconfig` resolution, no whole-program build). `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.
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 (47 tests)
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
  ```