@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 +1 -0
- package/dist/index.cjs +129 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +125 -14
- package/dist/index.js.map +1 -1
- package/package.json +10 -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,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
|
-
"
|
|
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
|
-
"
|
|
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"
|
|
1211
|
-
path: { type: "string"
|
|
1302
|
+
symbol: { type: "string" },
|
|
1303
|
+
path: { type: "string" },
|
|
1212
1304
|
direction: {
|
|
1213
1305
|
type: "string",
|
|
1214
|
-
enum: ["incoming", "outgoing"
|
|
1215
|
-
default: "
|
|
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
|
}
|