@aiready/ast-mcp-server 0.4.0 → 0.4.2

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
@@ -23,6 +23,7 @@ This Model Context Protocol (MCP) server provides high-precision, AST-aware tool
23
23
  | `get_file_structure` | Return a structural tree of a file (classes, methods, enums). |
24
24
  | `search_code` | Blazingly fast regex search via bundled ripgrep. |
25
25
  | `get_symbol_docs` | Extract full JSDoc/TSDoc metadata for any symbol. |
26
+ | `get_call_hierarchy` | Find callers and callees for a symbol (incoming/outgoing). |
26
27
  | `build_symbol_index` | Warm the disk cache for a project (highly recommended). |
27
28
 
28
29
  ## 📦 Installation
package/dist/index.cjs CHANGED
@@ -31,10 +31,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  ));
32
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
33
 
34
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js
34
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@6.0.2_yaml@2.8.3/node_modules/tsup/assets/cjs_shims.js
35
35
  var getImportMetaUrl, importMetaUrl;
36
36
  var init_cjs_shims = __esm({
37
- "../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js"() {
37
+ "../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@6.0.2_yaml@2.8.3/node_modules/tsup/assets/cjs_shims.js"() {
38
38
  "use strict";
39
39
  getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
40
40
  importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
@@ -182,10 +182,17 @@ var GetCallHierarchySchema = import_zod.z.object({
182
182
  path: import_zod.z.string().describe("Project root directory"),
183
183
  direction: import_zod.z.enum(["incoming", "outgoing", "both"]).optional().default("both").describe("Direction of calls (default: both)")
184
184
  });
185
+ var CheckSymbolGroundingSchema = import_zod.z.object({
186
+ symbol: import_zod.z.string().describe("Symbol name to assess grounding for"),
187
+ path: import_zod.z.string().describe("Project root directory")
188
+ });
185
189
 
186
190
  // src/tools/resolve-definition.ts
187
191
  init_cjs_shims();
188
192
 
193
+ // src/utils/tool-utils.ts
194
+ init_cjs_shims();
195
+
189
196
  // src/adapters/typescript-adapter.ts
190
197
  init_cjs_shims();
191
198
  var import_ts_morph3 = require("ts-morph");
@@ -373,7 +380,7 @@ var SymbolIndex = class {
373
380
  const startTime = Date.now();
374
381
  const safeRoot = validateWorkspacePath(rootDir);
375
382
  const cachePath = this.getCachePath(safeRoot);
376
- if (import_fs2.default.existsSync(cachePath)) {
383
+ if (import_fs2.default.existsSync(cachePath) && process.env.AST_DISABLE_CACHE !== "true") {
377
384
  try {
378
385
  const cached = JSON.parse(
379
386
  import_fs2.default.readFileSync(cachePath, "utf-8")
@@ -487,6 +494,7 @@ var WorkerPool = class {
487
494
  constructor(poolSize) {
488
495
  this.poolSize = poolSize;
489
496
  }
497
+ poolSize;
490
498
  workers = [];
491
499
  available = [];
492
500
  queue = [];
@@ -497,8 +505,8 @@ var WorkerPool = class {
497
505
  const workerPath = import_path4.default.join(__dirname, "ast-worker.js");
498
506
  for (let i = 0; i < this.poolSize; i++) {
499
507
  const worker = new import_worker_threads.Worker(workerPath);
500
- worker.on("message", (msg) => this.handleResult(msg));
501
- worker.on("error", (err) => this.handleWorkerError(worker, err));
508
+ worker.on("message", (msg) => this.handleResult(worker, msg));
509
+ worker.on("error", (_err) => this.handleWorkerError(worker, _err));
502
510
  this.workers.push(worker);
503
511
  this.available.push(worker);
504
512
  }
@@ -519,7 +527,7 @@ var WorkerPool = class {
519
527
  this.activeJobs.set(task.id, task);
520
528
  worker.postMessage({ id: task.id, type: task.type, payload: task.payload });
521
529
  }
522
- handleResult(msg) {
530
+ handleResult(worker, msg) {
523
531
  const task = this.activeJobs.get(msg.id);
524
532
  if (!task) return;
525
533
  this.activeJobs.delete(msg.id);
@@ -528,6 +536,12 @@ var WorkerPool = class {
528
536
  } else {
529
537
  task.resolve(msg.result);
530
538
  }
539
+ const nextTask = this.queue.shift();
540
+ if (nextTask) {
541
+ this.dispatch(worker, nextTask);
542
+ } else {
543
+ this.available.push(worker);
544
+ }
531
545
  }
532
546
  handleWorkerError(worker, err) {
533
547
  const idx = this.workers.indexOf(worker);
@@ -536,7 +550,7 @@ var WorkerPool = class {
536
550
  const __dirname = import_path4.default.dirname((0, import_url.fileURLToPath)(importMetaUrl));
537
551
  const workerPath = import_path4.default.join(__dirname, "ast-worker.js");
538
552
  const newWorker = new import_worker_threads.Worker(workerPath);
539
- newWorker.on("message", (msg) => this.handleResult(msg));
553
+ newWorker.on("message", (msg) => this.handleResult(newWorker, msg));
540
554
  newWorker.on("error", (_e) => this.handleWorkerError(newWorker, _e));
541
555
  this.workers[idx] = newWorker;
542
556
  this.available.push(newWorker);
@@ -635,16 +649,12 @@ var TypeScriptAdapter = class {
635
649
  const project = projectManager.ensureProject(tsconfig);
636
650
  const sourceFile = project.addSourceFileAtPathIfExists(hit.file);
637
651
  if (!sourceFile) return { references: [], total_count: 0 };
638
- const exported = sourceFile.getExportedDeclarations().get(symbolName);
639
- if (!exported || exported.length === 0)
640
- return { references: [], total_count: 0 };
641
- const targetNode = exported[0];
642
652
  try {
643
653
  const { searchCode: searchCode2 } = await Promise.resolve().then(() => (init_search_code(), search_code_exports));
644
654
  const searchResults = await searchCode2(
645
655
  symbolName,
646
656
  path6,
647
- "*.{ts,tsx,js,jsx}",
657
+ "**/*.{ts,tsx,js,jsx}",
648
658
  1e3,
649
659
  false
650
660
  );
@@ -652,8 +662,13 @@ var TypeScriptAdapter = class {
652
662
  for (const file of filesToLoad) {
653
663
  project.addSourceFileAtPathIfExists(file);
654
664
  }
665
+ project.resolveSourceFileDependencies();
655
666
  } catch (_e) {
656
667
  }
668
+ const exported = sourceFile.getExportedDeclarations().get(symbolName);
669
+ if (!exported || exported.length === 0)
670
+ return { references: [], total_count: 0 };
671
+ const targetNode = exported[0];
657
672
  const refSymbols = "findReferences" in targetNode && typeof targetNode.findReferences === "function" ? targetNode.findReferences() : void 0;
658
673
  if (!refSymbols) return { references: [], total_count: 0 };
659
674
  const results = [];
@@ -709,7 +724,7 @@ var TypeScriptAdapter = class {
709
724
  const searchResults = await searchCode2(
710
725
  symbolName,
711
726
  path6,
712
- "*.{ts,tsx,js,jsx}",
727
+ "**/*.{ts,tsx,js,jsx}",
713
728
  1e3,
714
729
  false
715
730
  );
@@ -886,21 +901,32 @@ var TypeScriptAdapter = class {
886
901
  };
887
902
  var typescriptAdapter = new TypeScriptAdapter();
888
903
 
904
+ // src/utils/tool-utils.ts
905
+ async function wrapAdapterCall(methodName, symbol, path6, ...args) {
906
+ const method = typescriptAdapter[methodName];
907
+ return await method.apply(typescriptAdapter, [symbol, path6, ...args]);
908
+ }
909
+
889
910
  // src/tools/resolve-definition.ts
890
911
  async function resolveDefinition(symbol, path6) {
891
- return await typescriptAdapter.resolveDefinition(symbol, path6);
912
+ return await wrapAdapterCall(
913
+ "resolveDefinition",
914
+ symbol,
915
+ path6
916
+ );
892
917
  }
893
918
 
894
919
  // src/tools/find-references.ts
895
920
  init_cjs_shims();
896
921
  async function findReferences(symbol, path6, limit = 50, offset = 0) {
897
- return await typescriptAdapter.findReferences(symbol, path6, limit, offset);
922
+ return await wrapAdapterCall("findReferences", symbol, path6, limit, offset);
898
923
  }
899
924
 
900
925
  // src/tools/find-implementations.ts
901
926
  init_cjs_shims();
902
927
  async function findImplementations(symbol, path6, limit = 50, offset = 0) {
903
- return await typescriptAdapter.findImplementations(
928
+ return await wrapAdapterCall(
929
+ "findImplementations",
904
930
  symbol,
905
931
  path6,
906
932
  limit,
@@ -1030,6 +1056,86 @@ async function getCallHierarchy(symbolName, rootDir, direction = "both") {
1030
1056
  return results;
1031
1057
  }
1032
1058
 
1059
+ // src/tools/check-symbol-grounding.ts
1060
+ init_cjs_shims();
1061
+ var import_ts_morph6 = require("ts-morph");
1062
+ init_security();
1063
+ async function checkSymbolGrounding(symbol, filePath) {
1064
+ const safePath = validateWorkspacePath(filePath);
1065
+ const tsconfig = await projectManager.findNearestTsConfig(safePath);
1066
+ if (!tsconfig) return void 0;
1067
+ const project = projectManager.ensureProject(tsconfig);
1068
+ const sourceFile = project.addSourceFileAtPathIfExists(safePath);
1069
+ if (!sourceFile) return void 0;
1070
+ const node = sourceFile.getDescendantsOfKind(import_ts_morph6.SyntaxKind.Identifier).find((id) => id.getText() === symbol);
1071
+ if (!node) return void 0;
1072
+ const decl = node.getSymbol()?.getDeclarations()?.[0];
1073
+ if (!decl) return void 0;
1074
+ let docScore = 0;
1075
+ let typeScore = 0;
1076
+ let depthScore = 30;
1077
+ const recommendations = [];
1078
+ const docs = typescriptAdapter.getSymbolDocs(decl);
1079
+ if (docs) {
1080
+ docScore += 10;
1081
+ if (docs.documentation && docs.documentation.length > 20) docScore += 10;
1082
+ const hasParams = docs.tags.some((t) => t.name === "param");
1083
+ const hasReturns = docs.tags.some(
1084
+ (t) => t.name === "returns" || t.name === "return"
1085
+ );
1086
+ if (hasParams) docScore += 10;
1087
+ else
1088
+ recommendations.push(
1089
+ "Add @param tags to document implementation details for the agent."
1090
+ );
1091
+ if (hasReturns) docScore += 10;
1092
+ else
1093
+ recommendations.push("Add @returns tag to clarify output expectations.");
1094
+ } else {
1095
+ recommendations.push(
1096
+ "Missing JSDoc. Agents need explicit documentation to understand intent without reading the source."
1097
+ );
1098
+ }
1099
+ const typeText = decl.getType?.()?.getText?.() || "";
1100
+ if (typeText) {
1101
+ if (!typeText.includes("any")) typeScore += 15;
1102
+ else
1103
+ recommendations.push(
1104
+ 'Avoid "any" types. They break agentic reasoning and cause hallucinations.'
1105
+ );
1106
+ if (!typeText.includes("unknown")) typeScore += 15;
1107
+ else
1108
+ recommendations.push(
1109
+ 'Use specific interfaces instead of "unknown" to minimize agent probe requirements.'
1110
+ );
1111
+ }
1112
+ const importCount = sourceFile.getImportDeclarations().length;
1113
+ if (importCount > 10) {
1114
+ const penalty = Math.min(20, (importCount - 10) * 2);
1115
+ depthScore -= penalty;
1116
+ recommendations.push(
1117
+ `High dependency depth (${importCount} imports). Agents must load more files to understand this symbol's context.`
1118
+ );
1119
+ }
1120
+ const totalScore = docScore + typeScore + depthScore;
1121
+ let grade = "F";
1122
+ if (totalScore >= 90) grade = "S (Agent-Native)";
1123
+ else if (totalScore >= 80) grade = "A (Highly Grounded)";
1124
+ else if (totalScore >= 70) grade = "B (Good)";
1125
+ else if (totalScore >= 60) grade = "C (Acceptable)";
1126
+ else if (totalScore >= 40) grade = "D (Poor)";
1127
+ return {
1128
+ score: totalScore,
1129
+ grade,
1130
+ breakdown: {
1131
+ documentation: docScore,
1132
+ typeClarity: typeScore,
1133
+ depthImpact: depthScore
1134
+ },
1135
+ recommendations
1136
+ };
1137
+ }
1138
+
1033
1139
  // src/index.ts
1034
1140
  var import_types2 = require("@modelcontextprotocol/sdk/types.js");
1035
1141
  var ASTExplorerServer = class {
@@ -1193,16 +1299,28 @@ var ASTExplorerServer = class {
1193
1299
  inputSchema: {
1194
1300
  type: "object",
1195
1301
  properties: {
1196
- symbol: { type: "string", description: "Symbol name" },
1197
- path: { type: "string", description: "Project root" },
1302
+ symbol: { type: "string" },
1303
+ path: { type: "string" },
1198
1304
  direction: {
1199
1305
  type: "string",
1200
- enum: ["incoming", "outgoing", "both"],
1201
- default: "both"
1306
+ enum: ["incoming", "outgoing"],
1307
+ default: "outgoing"
1202
1308
  }
1203
1309
  },
1204
1310
  required: ["symbol", "path"]
1205
1311
  }
1312
+ },
1313
+ {
1314
+ name: "check_symbol_grounding",
1315
+ description: "Assess a symbol's AI grounding quality (docs, type clarity, depth).",
1316
+ inputSchema: {
1317
+ type: "object",
1318
+ properties: {
1319
+ symbol: { type: "string" },
1320
+ path: { type: "string" }
1321
+ },
1322
+ required: ["symbol", "path"]
1323
+ }
1206
1324
  }
1207
1325
  ]
1208
1326
  };
@@ -1291,6 +1409,15 @@ var ASTExplorerServer = class {
1291
1409
  ]
1292
1410
  };
1293
1411
  }
1412
+ case "check_symbol_grounding": {
1413
+ const { symbol, path: path6 } = CheckSymbolGroundingSchema.parse(args);
1414
+ const result = await checkSymbolGrounding(symbol, path6);
1415
+ return {
1416
+ content: [
1417
+ { type: "text", text: JSON.stringify(result, null, 2) }
1418
+ ]
1419
+ };
1420
+ }
1294
1421
  default:
1295
1422
  throw new Error(`Unknown tool: ${name}`);
1296
1423
  }