@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 +1 -0
- package/dist/index.cjs +147 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +140 -18
- package/dist/index.js.map +1 -1
- package/package.json +12 -9
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@
|
|
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@
|
|
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", (
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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
|
|
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
|
|
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"
|
|
1197
|
-
path: { type: "string"
|
|
1302
|
+
symbol: { type: "string" },
|
|
1303
|
+
path: { type: "string" },
|
|
1198
1304
|
direction: {
|
|
1199
1305
|
type: "string",
|
|
1200
|
-
enum: ["incoming", "outgoing"
|
|
1201
|
-
default: "
|
|
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
|
}
|