@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/dist/index.js CHANGED
@@ -55,6 +55,10 @@ var GetCallHierarchySchema = z.object({
55
55
  path: z.string().describe("Project root directory"),
56
56
  direction: z.enum(["incoming", "outgoing", "both"]).optional().default("both").describe("Direction of calls (default: both)")
57
57
  });
58
+ var CheckSymbolGroundingSchema = z.object({
59
+ symbol: z.string().describe("Symbol name to assess grounding for"),
60
+ path: z.string().describe("Project root directory")
61
+ });
58
62
 
59
63
  // src/adapters/typescript-adapter.ts
60
64
  import { Node as Node2 } from "ts-morph";
@@ -238,7 +242,7 @@ var SymbolIndex = class {
238
242
  const startTime = Date.now();
239
243
  const safeRoot = validateWorkspacePath(rootDir);
240
244
  const cachePath = this.getCachePath(safeRoot);
241
- if (fs2.existsSync(cachePath)) {
245
+ if (fs2.existsSync(cachePath) && process.env.AST_DISABLE_CACHE !== "true") {
242
246
  try {
243
247
  const cached = JSON.parse(
244
248
  fs2.readFileSync(cachePath, "utf-8")
@@ -348,6 +352,7 @@ var WorkerPool = class {
348
352
  constructor(poolSize) {
349
353
  this.poolSize = poolSize;
350
354
  }
355
+ poolSize;
351
356
  workers = [];
352
357
  available = [];
353
358
  queue = [];
@@ -358,8 +363,8 @@ var WorkerPool = class {
358
363
  const workerPath = path3.join(__dirname2, "ast-worker.js");
359
364
  for (let i = 0; i < this.poolSize; i++) {
360
365
  const worker = new Worker(workerPath);
361
- worker.on("message", (msg) => this.handleResult(msg));
362
- worker.on("error", (err) => this.handleWorkerError(worker, err));
366
+ worker.on("message", (msg) => this.handleResult(worker, msg));
367
+ worker.on("error", (_err) => this.handleWorkerError(worker, _err));
363
368
  this.workers.push(worker);
364
369
  this.available.push(worker);
365
370
  }
@@ -380,7 +385,7 @@ var WorkerPool = class {
380
385
  this.activeJobs.set(task.id, task);
381
386
  worker.postMessage({ id: task.id, type: task.type, payload: task.payload });
382
387
  }
383
- handleResult(msg) {
388
+ handleResult(worker, msg) {
384
389
  const task = this.activeJobs.get(msg.id);
385
390
  if (!task) return;
386
391
  this.activeJobs.delete(msg.id);
@@ -389,6 +394,12 @@ var WorkerPool = class {
389
394
  } else {
390
395
  task.resolve(msg.result);
391
396
  }
397
+ const nextTask = this.queue.shift();
398
+ if (nextTask) {
399
+ this.dispatch(worker, nextTask);
400
+ } else {
401
+ this.available.push(worker);
402
+ }
392
403
  }
393
404
  handleWorkerError(worker, err) {
394
405
  const idx = this.workers.indexOf(worker);
@@ -397,7 +408,7 @@ var WorkerPool = class {
397
408
  const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
398
409
  const workerPath = path3.join(__dirname2, "ast-worker.js");
399
410
  const newWorker = new Worker(workerPath);
400
- newWorker.on("message", (msg) => this.handleResult(msg));
411
+ newWorker.on("message", (msg) => this.handleResult(newWorker, msg));
401
412
  newWorker.on("error", (_e) => this.handleWorkerError(newWorker, _e));
402
413
  this.workers[idx] = newWorker;
403
414
  this.available.push(newWorker);
@@ -496,16 +507,12 @@ var TypeScriptAdapter = class {
496
507
  const project = projectManager.ensureProject(tsconfig);
497
508
  const sourceFile = project.addSourceFileAtPathIfExists(hit.file);
498
509
  if (!sourceFile) return { references: [], total_count: 0 };
499
- const exported = sourceFile.getExportedDeclarations().get(symbolName);
500
- if (!exported || exported.length === 0)
501
- return { references: [], total_count: 0 };
502
- const targetNode = exported[0];
503
510
  try {
504
511
  const { searchCode: searchCode2 } = await import("./search-code-63QOEFQN.js");
505
512
  const searchResults = await searchCode2(
506
513
  symbolName,
507
514
  path5,
508
- "*.{ts,tsx,js,jsx}",
515
+ "**/*.{ts,tsx,js,jsx}",
509
516
  1e3,
510
517
  false
511
518
  );
@@ -513,8 +520,13 @@ var TypeScriptAdapter = class {
513
520
  for (const file of filesToLoad) {
514
521
  project.addSourceFileAtPathIfExists(file);
515
522
  }
523
+ project.resolveSourceFileDependencies();
516
524
  } catch (_e) {
517
525
  }
526
+ const exported = sourceFile.getExportedDeclarations().get(symbolName);
527
+ if (!exported || exported.length === 0)
528
+ return { references: [], total_count: 0 };
529
+ const targetNode = exported[0];
518
530
  const refSymbols = "findReferences" in targetNode && typeof targetNode.findReferences === "function" ? targetNode.findReferences() : void 0;
519
531
  if (!refSymbols) return { references: [], total_count: 0 };
520
532
  const results = [];
@@ -570,7 +582,7 @@ var TypeScriptAdapter = class {
570
582
  const searchResults = await searchCode2(
571
583
  symbolName,
572
584
  path5,
573
- "*.{ts,tsx,js,jsx}",
585
+ "**/*.{ts,tsx,js,jsx}",
574
586
  1e3,
575
587
  false
576
588
  );
@@ -747,19 +759,30 @@ var TypeScriptAdapter = class {
747
759
  };
748
760
  var typescriptAdapter = new TypeScriptAdapter();
749
761
 
762
+ // src/utils/tool-utils.ts
763
+ async function wrapAdapterCall(methodName, symbol, path5, ...args) {
764
+ const method = typescriptAdapter[methodName];
765
+ return await method.apply(typescriptAdapter, [symbol, path5, ...args]);
766
+ }
767
+
750
768
  // src/tools/resolve-definition.ts
751
769
  async function resolveDefinition(symbol, path5) {
752
- return await typescriptAdapter.resolveDefinition(symbol, path5);
770
+ return await wrapAdapterCall(
771
+ "resolveDefinition",
772
+ symbol,
773
+ path5
774
+ );
753
775
  }
754
776
 
755
777
  // src/tools/find-references.ts
756
778
  async function findReferences(symbol, path5, limit = 50, offset = 0) {
757
- return await typescriptAdapter.findReferences(symbol, path5, limit, offset);
779
+ return await wrapAdapterCall("findReferences", symbol, path5, limit, offset);
758
780
  }
759
781
 
760
782
  // src/tools/find-implementations.ts
761
783
  async function findImplementations(symbol, path5, limit = 50, offset = 0) {
762
- return await typescriptAdapter.findImplementations(
784
+ return await wrapAdapterCall(
785
+ "findImplementations",
763
786
  symbol,
764
787
  path5,
765
788
  limit,
@@ -880,6 +903,84 @@ async function getCallHierarchy(symbolName, rootDir, direction = "both") {
880
903
  return results;
881
904
  }
882
905
 
906
+ // src/tools/check-symbol-grounding.ts
907
+ import { SyntaxKind as SyntaxKind3 } from "ts-morph";
908
+ async function checkSymbolGrounding(symbol, filePath) {
909
+ const safePath = validateWorkspacePath(filePath);
910
+ const tsconfig = await projectManager.findNearestTsConfig(safePath);
911
+ if (!tsconfig) return void 0;
912
+ const project = projectManager.ensureProject(tsconfig);
913
+ const sourceFile = project.addSourceFileAtPathIfExists(safePath);
914
+ if (!sourceFile) return void 0;
915
+ const node = sourceFile.getDescendantsOfKind(SyntaxKind3.Identifier).find((id) => id.getText() === symbol);
916
+ if (!node) return void 0;
917
+ const decl = node.getSymbol()?.getDeclarations()?.[0];
918
+ if (!decl) return void 0;
919
+ let docScore = 0;
920
+ let typeScore = 0;
921
+ let depthScore = 30;
922
+ const recommendations = [];
923
+ const docs = typescriptAdapter.getSymbolDocs(decl);
924
+ if (docs) {
925
+ docScore += 10;
926
+ if (docs.documentation && docs.documentation.length > 20) docScore += 10;
927
+ const hasParams = docs.tags.some((t) => t.name === "param");
928
+ const hasReturns = docs.tags.some(
929
+ (t) => t.name === "returns" || t.name === "return"
930
+ );
931
+ if (hasParams) docScore += 10;
932
+ else
933
+ recommendations.push(
934
+ "Add @param tags to document implementation details for the agent."
935
+ );
936
+ if (hasReturns) docScore += 10;
937
+ else
938
+ recommendations.push("Add @returns tag to clarify output expectations.");
939
+ } else {
940
+ recommendations.push(
941
+ "Missing JSDoc. Agents need explicit documentation to understand intent without reading the source."
942
+ );
943
+ }
944
+ const typeText = decl.getType?.()?.getText?.() || "";
945
+ if (typeText) {
946
+ if (!typeText.includes("any")) typeScore += 15;
947
+ else
948
+ recommendations.push(
949
+ 'Avoid "any" types. They break agentic reasoning and cause hallucinations.'
950
+ );
951
+ if (!typeText.includes("unknown")) typeScore += 15;
952
+ else
953
+ recommendations.push(
954
+ 'Use specific interfaces instead of "unknown" to minimize agent probe requirements.'
955
+ );
956
+ }
957
+ const importCount = sourceFile.getImportDeclarations().length;
958
+ if (importCount > 10) {
959
+ const penalty = Math.min(20, (importCount - 10) * 2);
960
+ depthScore -= penalty;
961
+ recommendations.push(
962
+ `High dependency depth (${importCount} imports). Agents must load more files to understand this symbol's context.`
963
+ );
964
+ }
965
+ const totalScore = docScore + typeScore + depthScore;
966
+ let grade = "F";
967
+ if (totalScore >= 90) grade = "S (Agent-Native)";
968
+ else if (totalScore >= 80) grade = "A (Highly Grounded)";
969
+ else if (totalScore >= 70) grade = "B (Good)";
970
+ else if (totalScore >= 60) grade = "C (Acceptable)";
971
+ else if (totalScore >= 40) grade = "D (Poor)";
972
+ return {
973
+ score: totalScore,
974
+ grade,
975
+ breakdown: {
976
+ documentation: docScore,
977
+ typeClarity: typeScore,
978
+ depthImpact: depthScore
979
+ },
980
+ recommendations
981
+ };
982
+ }
983
+
883
984
  // src/index.ts
884
985
  import {
885
986
  ListResourcesRequestSchema,
@@ -1046,16 +1147,28 @@ var ASTExplorerServer = class {
1046
1147
  inputSchema: {
1047
1148
  type: "object",
1048
1149
  properties: {
1049
- symbol: { type: "string", description: "Symbol name" },
1050
- path: { type: "string", description: "Project root" },
1150
+ symbol: { type: "string" },
1151
+ path: { type: "string" },
1051
1152
  direction: {
1052
1153
  type: "string",
1053
- enum: ["incoming", "outgoing", "both"],
1054
- default: "both"
1154
+ enum: ["incoming", "outgoing"],
1155
+ default: "outgoing"
1055
1156
  }
1056
1157
  },
1057
1158
  required: ["symbol", "path"]
1058
1159
  }
1160
+ },
1161
+ {
1162
+ name: "check_symbol_grounding",
1163
+ description: "Assess a symbol's AI grounding quality (docs, type clarity, depth).",
1164
+ inputSchema: {
1165
+ type: "object",
1166
+ properties: {
1167
+ symbol: { type: "string" },
1168
+ path: { type: "string" }
1169
+ },
1170
+ required: ["symbol", "path"]
1171
+ }
1059
1172
  }
1060
1173
  ]
1061
1174
  };
@@ -1144,6 +1257,15 @@ var ASTExplorerServer = class {
1144
1257
  ]
1145
1258
  };
1146
1259
  }
1260
+ case "check_symbol_grounding": {
1261
+ const { symbol, path: path5 } = CheckSymbolGroundingSchema.parse(args);
1262
+ const result = await checkSymbolGrounding(symbol, path5);
1263
+ return {
1264
+ content: [
1265
+ { type: "text", text: JSON.stringify(result, null, 2) }
1266
+ ]
1267
+ };
1268
+ }
1147
1269
  default:
1148
1270
  throw new Error(`Unknown tool: ${name}`);
1149
1271
  }