@aiready/ast-mcp-server 0.4.1 → 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,6 +182,10 @@ 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();
@@ -376,7 +380,7 @@ var SymbolIndex = class {
376
380
  const startTime = Date.now();
377
381
  const safeRoot = validateWorkspacePath(rootDir);
378
382
  const cachePath = this.getCachePath(safeRoot);
379
- if (import_fs2.default.existsSync(cachePath)) {
383
+ if (import_fs2.default.existsSync(cachePath) && process.env.AST_DISABLE_CACHE !== "true") {
380
384
  try {
381
385
  const cached = JSON.parse(
382
386
  import_fs2.default.readFileSync(cachePath, "utf-8")
@@ -490,6 +494,7 @@ var WorkerPool = class {
490
494
  constructor(poolSize) {
491
495
  this.poolSize = poolSize;
492
496
  }
497
+ poolSize;
493
498
  workers = [];
494
499
  available = [];
495
500
  queue = [];
@@ -500,7 +505,7 @@ var WorkerPool = class {
500
505
  const workerPath = import_path4.default.join(__dirname, "ast-worker.js");
501
506
  for (let i = 0; i < this.poolSize; i++) {
502
507
  const worker = new import_worker_threads.Worker(workerPath);
503
- worker.on("message", (msg) => this.handleResult(msg));
508
+ worker.on("message", (msg) => this.handleResult(worker, msg));
504
509
  worker.on("error", (_err) => this.handleWorkerError(worker, _err));
505
510
  this.workers.push(worker);
506
511
  this.available.push(worker);
@@ -522,7 +527,7 @@ var WorkerPool = class {
522
527
  this.activeJobs.set(task.id, task);
523
528
  worker.postMessage({ id: task.id, type: task.type, payload: task.payload });
524
529
  }
525
- handleResult(msg) {
530
+ handleResult(worker, msg) {
526
531
  const task = this.activeJobs.get(msg.id);
527
532
  if (!task) return;
528
533
  this.activeJobs.delete(msg.id);
@@ -531,6 +536,12 @@ var WorkerPool = class {
531
536
  } else {
532
537
  task.resolve(msg.result);
533
538
  }
539
+ const nextTask = this.queue.shift();
540
+ if (nextTask) {
541
+ this.dispatch(worker, nextTask);
542
+ } else {
543
+ this.available.push(worker);
544
+ }
534
545
  }
535
546
  handleWorkerError(worker, err) {
536
547
  const idx = this.workers.indexOf(worker);
@@ -539,7 +550,7 @@ var WorkerPool = class {
539
550
  const __dirname = import_path4.default.dirname((0, import_url.fileURLToPath)(importMetaUrl));
540
551
  const workerPath = import_path4.default.join(__dirname, "ast-worker.js");
541
552
  const newWorker = new import_worker_threads.Worker(workerPath);
542
- newWorker.on("message", (msg) => this.handleResult(msg));
553
+ newWorker.on("message", (msg) => this.handleResult(newWorker, msg));
543
554
  newWorker.on("error", (_e) => this.handleWorkerError(newWorker, _e));
544
555
  this.workers[idx] = newWorker;
545
556
  this.available.push(newWorker);
@@ -638,16 +649,12 @@ var TypeScriptAdapter = class {
638
649
  const project = projectManager.ensureProject(tsconfig);
639
650
  const sourceFile = project.addSourceFileAtPathIfExists(hit.file);
640
651
  if (!sourceFile) return { references: [], total_count: 0 };
641
- const exported = sourceFile.getExportedDeclarations().get(symbolName);
642
- if (!exported || exported.length === 0)
643
- return { references: [], total_count: 0 };
644
- const targetNode = exported[0];
645
652
  try {
646
653
  const { searchCode: searchCode2 } = await Promise.resolve().then(() => (init_search_code(), search_code_exports));
647
654
  const searchResults = await searchCode2(
648
655
  symbolName,
649
656
  path6,
650
- "*.{ts,tsx,js,jsx}",
657
+ "**/*.{ts,tsx,js,jsx}",
651
658
  1e3,
652
659
  false
653
660
  );
@@ -655,8 +662,13 @@ var TypeScriptAdapter = class {
655
662
  for (const file of filesToLoad) {
656
663
  project.addSourceFileAtPathIfExists(file);
657
664
  }
665
+ project.resolveSourceFileDependencies();
658
666
  } catch (_e) {
659
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];
660
672
  const refSymbols = "findReferences" in targetNode && typeof targetNode.findReferences === "function" ? targetNode.findReferences() : void 0;
661
673
  if (!refSymbols) return { references: [], total_count: 0 };
662
674
  const results = [];
@@ -712,7 +724,7 @@ var TypeScriptAdapter = class {
712
724
  const searchResults = await searchCode2(
713
725
  symbolName,
714
726
  path6,
715
- "*.{ts,tsx,js,jsx}",
727
+ "**/*.{ts,tsx,js,jsx}",
716
728
  1e3,
717
729
  false
718
730
  );
@@ -1044,6 +1056,86 @@ async function getCallHierarchy(symbolName, rootDir, direction = "both") {
1044
1056
  return results;
1045
1057
  }
1046
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
+
1047
1139
  // src/index.ts
1048
1140
  var import_types2 = require("@modelcontextprotocol/sdk/types.js");
1049
1141
  var ASTExplorerServer = class {
@@ -1207,16 +1299,28 @@ var ASTExplorerServer = class {
1207
1299
  inputSchema: {
1208
1300
  type: "object",
1209
1301
  properties: {
1210
- symbol: { type: "string", description: "Symbol name" },
1211
- path: { type: "string", description: "Project root" },
1302
+ symbol: { type: "string" },
1303
+ path: { type: "string" },
1212
1304
  direction: {
1213
1305
  type: "string",
1214
- enum: ["incoming", "outgoing", "both"],
1215
- default: "both"
1306
+ enum: ["incoming", "outgoing"],
1307
+ default: "outgoing"
1216
1308
  }
1217
1309
  },
1218
1310
  required: ["symbol", "path"]
1219
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
+ }
1220
1324
  }
1221
1325
  ]
1222
1326
  };
@@ -1305,6 +1409,15 @@ var ASTExplorerServer = class {
1305
1409
  ]
1306
1410
  };
1307
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
+ }
1308
1421
  default:
1309
1422
  throw new Error(`Unknown tool: ${name}`);
1310
1423
  }