universal-ast-mapper 1.4.0 → 1.6.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/README.md CHANGED
@@ -562,6 +562,18 @@ src/
562
562
 
563
563
  ---
564
564
 
565
+ ## MCP resources
566
+
567
+ Beyond tools, the server exposes the codebase as **browseable MCP resources**, so an agent (or MCP client UI) can list and read structure directly:
568
+
569
+ | URI | What |
570
+ |-----|------|
571
+ | `ast://languages` | supported languages + extensions |
572
+ | `ast://skeleton/{path}` | the skeleton for one source file (templated; `resources/list` enumerates every file) |
573
+ | `ast://graph` | the whole-root symbol dependency graph (guarded by file count) |
574
+
575
+ ---
576
+
565
577
  ## GitHub Action — architecture gate in CI
566
578
 
567
579
  Use AST-MCP as a CI check with the bundled composite action (`action.yml`):
@@ -605,6 +617,8 @@ Not part of the public API: the internal `src/` module layout and the generated
605
617
 
606
618
  | Version | What changed |
607
619
  |---------|--------------|
620
+ | **1.6.0** | **MCP resource endpoints** — the server now exposes browseable resources: `ast://languages`, `ast://skeleton/{path}` (templated, one per source file via `resources/list`), and `ast://graph`. Agents can list and read codebase structure as resources, not just call tools. |
621
+ | **1.5.0** | **`.d.ts` / ambient declarations** — `declare function/const/class`, `declare module "x"`, and `declare namespace` (and plain `namespace`) are now extracted (previously a `.d.ts` yielded 0 symbols). Adds a `namespace` symbol kind; declared APIs surface as exported, nested under their module/namespace. |
608
622
  | **1.4.0** | **Dynamic import tracking** — dynamic `import("...")` and CommonJS `require("...")` calls (anywhere in a file) are now captured as imports with an `isDynamic` flag. Relative dynamic imports resolve and draw graph edges like static ones, so lazy-loaded routes/modules show up in the dependency graph. |
609
623
  | **1.3.0** | **TS/JS decorators** — class and method symbols now carry a `decorators` field (`@Component({...})`, `@Injectable()`, `@Get("/x")`), in skeletons and `get_call_graph`. Extends the Python decorator support (v0.8.7) to TypeScript/JavaScript — traces Angular/NestJS-style framework wiring to its class/handler. |
610
624
  | **1.2.0** | **File-level cross-package resolution** — in a monorepo, bare imports of a workspace package (`@org/utils`, `@org/utils/sub`) now resolve to the actual source file (preferring `src/` over built `dist/`), so `resolve_imports` marks them in-project and `build_symbol_graph` draws cross-package edges. Builds on the v1.1.0 workspace discovery. |
@@ -147,6 +147,38 @@ function handle(node, exported, typeIndex) {
147
147
  }
148
148
  return null;
149
149
  }
150
+ case "ambient_declaration":
151
+ // `.d.ts` / `declare ...` — surface the declared API as exported.
152
+ return collect(namedChildren(node), true, typeIndex);
153
+ case "function_signature": {
154
+ const name = nameOf(node) ?? "(function)";
155
+ return makeSymbol({
156
+ name,
157
+ kind: "function",
158
+ node,
159
+ rawKind: node.type,
160
+ signature: node.text.replace(/\s+/g, " ").replace(/;\s*$/, "").trim(),
161
+ exported,
162
+ doc: leadingComment(node),
163
+ });
164
+ }
165
+ case "module": // declare module "name" { ... }
166
+ case "internal_module": { // namespace Name { ... }
167
+ const nameNode = node.childForFieldName("name");
168
+ const rawName = nameNode ? nameNode.text : "(namespace)";
169
+ const name = rawName.replace(/^['"`]|['"`]$/g, "");
170
+ const body = node.childForFieldName("body");
171
+ const children = body ? collect(namedChildren(body), false, typeIndex) : [];
172
+ return makeSymbol({
173
+ name,
174
+ kind: "namespace",
175
+ node,
176
+ rawKind: node.type,
177
+ exported,
178
+ doc: leadingComment(node),
179
+ children,
180
+ });
181
+ }
150
182
  default:
151
183
  return null;
152
184
  }
@@ -200,6 +232,18 @@ function fromVariableDeclaration(node, exported, typeIndex) {
200
232
  doc: leadingComment(node),
201
233
  }));
202
234
  }
235
+ else if (exported && !value && decl.childForFieldName("type")) {
236
+ // Ambient `declare const X: T` — no initializer, but part of the typed API.
237
+ out.push(makeSymbol({
238
+ name,
239
+ kind: "const",
240
+ node: decl,
241
+ rawKind: `${node.type}>declare`,
242
+ signature: decl.text.replace(/\s+/g, " ").trim().slice(0, 120),
243
+ exported: true,
244
+ doc: leadingComment(node),
245
+ }));
246
+ }
203
247
  }
204
248
  return out;
205
249
  }
package/dist/html.js CHANGED
@@ -9,6 +9,7 @@ const KIND_COLORS = {
9
9
  const: "#65a30d",
10
10
  var: "#ca8a04",
11
11
  field: "#64748b",
12
+ namespace: "#9333ea",
12
13
  };
13
14
  function esc(s) {
14
15
  return s
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
  import { z } from "zod";
7
7
  import { fileURLToPath } from "node:url";
@@ -983,6 +983,71 @@ function describeError(err) {
983
983
  }
984
984
  return err instanceof Error ? err.message : String(err);
985
985
  }
986
+ /* ─────────────────── MCP resources (browseable structure) ──────────────── */
987
+ server.registerResource("languages", "ast://languages", {
988
+ title: "Supported languages",
989
+ description: "Languages and file extensions this server can map.",
990
+ mimeType: "application/json",
991
+ }, async (uri) => ({
992
+ contents: [{
993
+ uri: uri.href,
994
+ mimeType: "application/json",
995
+ text: JSON.stringify({ root: ROOT, languages: supportedLanguages() }, null, 2),
996
+ }],
997
+ }));
998
+ server.registerResource("skeleton", new ResourceTemplate("ast://skeleton/{+path}", {
999
+ list: async () => {
1000
+ const opts = resolveOptions({ detail: "outline", emitHtml: false });
1001
+ const files = collectSourceFiles(ROOT, opts);
1002
+ return {
1003
+ resources: files.map((f) => {
1004
+ const rel = path.relative(ROOT, f).split(path.sep).join("/");
1005
+ return { uri: `ast://skeleton/${rel}`, name: rel, mimeType: "application/json" };
1006
+ }),
1007
+ };
1008
+ },
1009
+ }), {
1010
+ title: "File skeleton",
1011
+ description: "Normalized code skeleton (symbols, imports, ranges) for one source file.",
1012
+ mimeType: "application/json",
1013
+ }, async (uri, variables) => {
1014
+ const rel = decodeURIComponent(String(variables.path)).split(path.sep).join("/");
1015
+ const { abs, rel: safeRel } = resolveInRoot(rel);
1016
+ const opts = resolveOptions({ detail: "outline", emitHtml: false });
1017
+ const skel = await buildSkeleton(abs, safeRel.split(path.sep).join("/"), opts);
1018
+ return {
1019
+ contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(skel, null, 2) }],
1020
+ };
1021
+ });
1022
+ server.registerResource("graph", "ast://graph", {
1023
+ title: "Symbol dependency graph",
1024
+ description: "Symbol-level dependency graph for the whole root (guarded by node count).",
1025
+ mimeType: "application/json",
1026
+ }, async (uri) => {
1027
+ const opts = resolveOptions({ detail: "outline", emitHtml: false });
1028
+ const files = collectSourceFiles(ROOT, opts);
1029
+ if (files.length > 1500) {
1030
+ return {
1031
+ contents: [{
1032
+ uri: uri.href,
1033
+ mimeType: "application/json",
1034
+ text: JSON.stringify({ note: `Too large to inline (${files.length} files). Use build_symbol_graph on a subdirectory.`, files: files.length }, null, 2),
1035
+ }],
1036
+ };
1037
+ }
1038
+ const skels = [];
1039
+ for (const file of files) {
1040
+ const fileRel = path.relative(ROOT, file).split(path.sep).join("/");
1041
+ try {
1042
+ skels.push(await buildSkeleton(file, fileRel, opts));
1043
+ }
1044
+ catch { /* skip */ }
1045
+ }
1046
+ const graph = buildSymbolGraph(skels, ROOT);
1047
+ return {
1048
+ contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(graph, null, 2) }],
1049
+ };
1050
+ });
986
1051
  async function main() {
987
1052
  const transport = new StdioServerTransport();
988
1053
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-ast-mapper",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "MCP server that maps source files into a normalized code skeleton (JSON + HTML) using tree-sitter.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",