@aiready/ast-mcp-server 0.1.0 → 0.1.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.cjs ADDED
@@ -0,0 +1,1046 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // ../../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
+ var init_cjs_shims = __esm({
35
+ "../../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"() {
36
+ "use strict";
37
+ }
38
+ });
39
+
40
+ // src/security.ts
41
+ function resolveWorkspaceRoot() {
42
+ return process.env.AST_WORKSPACE_ROOT || process.cwd();
43
+ }
44
+ function validateWorkspacePath(inputPath) {
45
+ const root = resolveWorkspaceRoot();
46
+ const resolved = import_path.default.resolve(root, inputPath);
47
+ const normalized = import_path.default.normalize(resolved);
48
+ if (!normalized.startsWith(root)) {
49
+ throw new Error(
50
+ `Path traversal detected: ${inputPath} escapes workspace root`
51
+ );
52
+ }
53
+ if (normalized.includes("\0")) {
54
+ throw new Error("Path contains null bytes");
55
+ }
56
+ return normalized;
57
+ }
58
+ var import_path;
59
+ var init_security = __esm({
60
+ "src/security.ts"() {
61
+ "use strict";
62
+ init_cjs_shims();
63
+ import_path = __toESM(require("path"), 1);
64
+ }
65
+ });
66
+
67
+ // src/tools/search-code.ts
68
+ var search_code_exports = {};
69
+ __export(search_code_exports, {
70
+ searchCode: () => searchCode
71
+ });
72
+ async function searchCode(pattern, searchPath, filePattern, limit = 50, regex = true) {
73
+ const safePath = validateWorkspacePath(searchPath);
74
+ const args = [
75
+ "--json",
76
+ "--max-count",
77
+ limit.toString(),
78
+ "--max-columns",
79
+ "500"
80
+ ];
81
+ if (!regex) {
82
+ args.push("--fixed-strings");
83
+ }
84
+ args.push(pattern, safePath);
85
+ if (filePattern) {
86
+ args.push("--glob", filePattern);
87
+ }
88
+ args.push("--glob", "!**/node_modules/**");
89
+ args.push("--glob", "!**/dist/**");
90
+ args.push("--glob", "!**/.git/**");
91
+ try {
92
+ const { stdout } = await execFileAsync(import_ripgrep.rgPath, args);
93
+ const lines = stdout.split("\n").filter(Boolean);
94
+ const results = [];
95
+ for (const line of lines) {
96
+ const data = JSON.parse(line);
97
+ if (data.type === "match") {
98
+ const file = data.data.path.text;
99
+ const lineNumber = data.data.line_number;
100
+ const submatches = data.data.submatches;
101
+ for (const submatch of submatches) {
102
+ results.push({
103
+ file,
104
+ line: lineNumber,
105
+ column: submatch.start,
106
+ text: data.data.lines.text.trim()
107
+ });
108
+ }
109
+ }
110
+ }
111
+ return results.slice(0, limit);
112
+ } catch (error) {
113
+ if (error.code === 1) {
114
+ return [];
115
+ }
116
+ throw error;
117
+ }
118
+ }
119
+ var import_child_process, import_util, import_ripgrep, execFileAsync;
120
+ var init_search_code = __esm({
121
+ "src/tools/search-code.ts"() {
122
+ "use strict";
123
+ init_cjs_shims();
124
+ import_child_process = require("child_process");
125
+ import_util = require("util");
126
+ import_ripgrep = require("@vscode/ripgrep");
127
+ init_security();
128
+ execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
129
+ }
130
+ });
131
+
132
+ // src/index.ts
133
+ var index_exports = {};
134
+ __export(index_exports, {
135
+ ASTExplorerServer: () => ASTExplorerServer
136
+ });
137
+ module.exports = __toCommonJS(index_exports);
138
+ init_cjs_shims();
139
+ var import_server = require("@modelcontextprotocol/sdk/server/index.js");
140
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
141
+ var import_types = require("@modelcontextprotocol/sdk/types.js");
142
+
143
+ // src/schemas.ts
144
+ init_cjs_shims();
145
+ var import_zod = require("zod");
146
+ var ResolveDefinitionSchema = import_zod.z.object({
147
+ symbol: import_zod.z.string().describe(
148
+ "Symbol name to resolve (function, class, type, interface, etc.)"
149
+ ),
150
+ path: import_zod.z.string().describe("Project root or target directory")
151
+ });
152
+ var FindReferencesSchema = import_zod.z.object({
153
+ symbol: import_zod.z.string().describe("Symbol name to find references for"),
154
+ path: import_zod.z.string().describe("Project root directory"),
155
+ limit: import_zod.z.number().optional().default(50).describe("Max results per page (default 50)"),
156
+ offset: import_zod.z.number().optional().default(0).describe("Pagination offset")
157
+ });
158
+ var FindImplementationsSchema = import_zod.z.object({
159
+ symbol: import_zod.z.string().describe("Interface or abstract class name to find implementations for"),
160
+ path: import_zod.z.string().describe("Project root directory"),
161
+ limit: import_zod.z.number().optional().default(50).describe("Max results per page"),
162
+ offset: import_zod.z.number().optional().default(0).describe("Pagination offset")
163
+ });
164
+ var GetFileStructureSchema = import_zod.z.object({
165
+ file: import_zod.z.string().describe("Absolute path to the file to analyze")
166
+ });
167
+ var SearchCodeSchema = import_zod.z.object({
168
+ pattern: import_zod.z.string().describe("Search pattern (regex by default)"),
169
+ path: import_zod.z.string().describe("Directory to search in"),
170
+ filePattern: import_zod.z.string().optional().describe('Glob filter (e.g., "*.ts")'),
171
+ limit: import_zod.z.number().optional().default(50).describe("Max matches to return"),
172
+ regex: import_zod.z.boolean().optional().default(true).describe("Use regex mode (default true)")
173
+ });
174
+ var GetSymbolDocsSchema = import_zod.z.object({
175
+ symbol: import_zod.z.string().describe("Symbol name to get documentation for"),
176
+ path: import_zod.z.string().describe("Project root directory")
177
+ });
178
+ var BuildSymbolIndexSchema = import_zod.z.object({
179
+ path: import_zod.z.string().describe("Project root directory to index")
180
+ });
181
+
182
+ // src/tools/resolve-definition.ts
183
+ init_cjs_shims();
184
+
185
+ // src/adapters/typescript-adapter.ts
186
+ init_cjs_shims();
187
+ var import_ts_morph3 = require("ts-morph");
188
+ var import_fs3 = __toESM(require("fs"), 1);
189
+
190
+ // src/project-manager.ts
191
+ init_cjs_shims();
192
+ var import_ts_morph = require("ts-morph");
193
+ var import_path2 = __toESM(require("path"), 1);
194
+ var import_fs = __toESM(require("fs"), 1);
195
+ var import_glob = require("glob");
196
+ init_security();
197
+ var ProjectManager = class {
198
+ projects = /* @__PURE__ */ new Map();
199
+ tsconfigCache = /* @__PURE__ */ new Map();
200
+ accessOrder = [];
201
+ // MRU at front
202
+ maxProjects = 4;
203
+ /**
204
+ * Find all tsconfig.json files in a directory (recursive)
205
+ */
206
+ async findTsConfigs(rootDir) {
207
+ const safeRoot = validateWorkspacePath(rootDir);
208
+ if (this.tsconfigCache.has(safeRoot)) {
209
+ return this.tsconfigCache.get(safeRoot);
210
+ }
211
+ const configs = await (0, import_glob.glob)("**/tsconfig.json", {
212
+ cwd: safeRoot,
213
+ absolute: true,
214
+ ignore: ["**/node_modules/**", "**/dist/**"]
215
+ });
216
+ this.tsconfigCache.set(safeRoot, configs);
217
+ return configs;
218
+ }
219
+ /**
220
+ * Ensure a Project exists for a given tsconfig path, managing LRU cache
221
+ */
222
+ ensureProject(tsconfigPath) {
223
+ this.checkMemoryPressure();
224
+ if (this.projects.has(tsconfigPath)) {
225
+ this.moveToFront(tsconfigPath);
226
+ return this.projects.get(tsconfigPath);
227
+ }
228
+ while (this.projects.size >= this.maxProjects) {
229
+ const oldest = this.accessOrder.pop();
230
+ this.disposeProject(oldest);
231
+ }
232
+ const project = new import_ts_morph.Project({
233
+ tsConfigFilePath: tsconfigPath,
234
+ skipAddingFilesFromTsConfig: true
235
+ // KEY: don't load all files
236
+ });
237
+ this.projects.set(tsconfigPath, project);
238
+ this.accessOrder.unshift(tsconfigPath);
239
+ return project;
240
+ }
241
+ /**
242
+ * Move a tsconfig path to the front of the access order (MRU)
243
+ */
244
+ moveToFront(tsconfigPath) {
245
+ const index = this.accessOrder.indexOf(tsconfigPath);
246
+ if (index > -1) {
247
+ this.accessOrder.splice(index, 1);
248
+ this.accessOrder.unshift(tsconfigPath);
249
+ }
250
+ }
251
+ checkMemoryPressure() {
252
+ const heapUsed = process.memoryUsage().heapUsed / 1024 / 1024;
253
+ const maxHeap = parseInt(process.env.AST_MAX_HEAP_MB || "1536", 10);
254
+ while (heapUsed > maxHeap && this.projects.size > 1) {
255
+ const oldest = this.accessOrder.pop();
256
+ this.disposeProject(oldest);
257
+ }
258
+ }
259
+ disposeProject(tsconfigPath) {
260
+ const project = this.projects.get(tsconfigPath);
261
+ if (project) {
262
+ for (const sourceFile of project.getSourceFiles()) {
263
+ project.removeSourceFile(sourceFile);
264
+ }
265
+ this.projects.delete(tsconfigPath);
266
+ }
267
+ }
268
+ /**
269
+ * Find the nearest tsconfig.json for a file
270
+ */
271
+ async findNearestTsConfig(filePath) {
272
+ const safePath = validateWorkspacePath(filePath);
273
+ let currentDir = import_path2.default.dirname(safePath);
274
+ const root = import_path2.default.parse(currentDir).root;
275
+ while (currentDir !== root) {
276
+ const tsconfigPath = import_path2.default.join(currentDir, "tsconfig.json");
277
+ if (import_fs.default.existsSync(tsconfigPath)) {
278
+ return tsconfigPath;
279
+ }
280
+ currentDir = import_path2.default.dirname(currentDir);
281
+ }
282
+ return void 0;
283
+ }
284
+ /**
285
+ * Get all tsconfigs for a path
286
+ */
287
+ async getProjectsForPath(rootDir) {
288
+ return this.findTsConfigs(rootDir);
289
+ }
290
+ /**
291
+ * Dispose all projects to free memory
292
+ */
293
+ disposeAll() {
294
+ for (const [key] of this.projects) {
295
+ this.disposeProject(key);
296
+ }
297
+ this.accessOrder = [];
298
+ }
299
+ };
300
+ var projectManager = new ProjectManager();
301
+
302
+ // src/index/symbol-index.ts
303
+ init_cjs_shims();
304
+ init_security();
305
+ var import_fs2 = __toESM(require("fs"), 1);
306
+ var import_crypto = __toESM(require("crypto"), 1);
307
+ var import_path3 = __toESM(require("path"), 1);
308
+ var import_ts_morph2 = require("ts-morph");
309
+ var SymbolIndex = class {
310
+ index = {};
311
+ getCachePath(rootDir) {
312
+ const hash = import_crypto.default.createHash("sha256").update(rootDir).digest("hex").slice(0, 12);
313
+ return import_path3.default.join(
314
+ process.platform === "win32" ? process.env.TEMP || "c:/temp" : "/tmp",
315
+ `ast-index-${hash}.json`
316
+ );
317
+ }
318
+ isCacheValid(cached, rootDir) {
319
+ if (cached.rootDir !== rootDir) return false;
320
+ for (const [file, cachedMtime] of Object.entries(cached.fileHashes)) {
321
+ if (!import_fs2.default.existsSync(file)) return false;
322
+ if (import_fs2.default.statSync(file).mtimeMs !== cachedMtime) return false;
323
+ }
324
+ return true;
325
+ }
326
+ computeStats(cache, duration_ms, memory_mb) {
327
+ const files = Object.keys(cache.fileHashes).length;
328
+ let functions = 0;
329
+ let classes = 0;
330
+ let interfaces = 0;
331
+ let types = 0;
332
+ for (const entries of Object.values(cache.symbols)) {
333
+ for (const entry of entries) {
334
+ if (entry.kind === "function" || entry.kind === "method") functions++;
335
+ if (entry.kind === "class") classes++;
336
+ if (entry.kind === "interface") interfaces++;
337
+ if (entry.kind === "type_alias") types++;
338
+ }
339
+ }
340
+ return {
341
+ indexed: {
342
+ files,
343
+ functions,
344
+ classes,
345
+ interfaces,
346
+ types
347
+ },
348
+ duration_ms: duration_ms || 0,
349
+ memory_mb: memory_mb || 0
350
+ };
351
+ }
352
+ mapKind(node) {
353
+ if (import_ts_morph2.Node.isClassDeclaration(node)) return "class";
354
+ if (import_ts_morph2.Node.isFunctionDeclaration(node)) return "function";
355
+ if (import_ts_morph2.Node.isInterfaceDeclaration(node)) return "interface";
356
+ if (import_ts_morph2.Node.isTypeAliasDeclaration(node)) return "type_alias";
357
+ if (import_ts_morph2.Node.isEnumDeclaration(node)) return "enum";
358
+ if (import_ts_morph2.Node.isVariableDeclaration(node)) return "variable";
359
+ if (import_ts_morph2.Node.isMethodDeclaration(node)) return "method";
360
+ if (import_ts_morph2.Node.isPropertyDeclaration(node)) return "property";
361
+ if (import_ts_morph2.Node.isParameterDeclaration(node)) return "parameter";
362
+ return "variable";
363
+ }
364
+ /**
365
+ * Build/Warm the index for a given path
366
+ */
367
+ async buildIndex(rootDir) {
368
+ const startTime = Date.now();
369
+ const safeRoot = validateWorkspacePath(rootDir);
370
+ const cachePath = this.getCachePath(safeRoot);
371
+ if (import_fs2.default.existsSync(cachePath)) {
372
+ try {
373
+ const cached = JSON.parse(
374
+ import_fs2.default.readFileSync(cachePath, "utf-8")
375
+ );
376
+ if (this.isCacheValid(cached, safeRoot)) {
377
+ this.index = cached.symbols;
378
+ return this.computeStats(
379
+ cached,
380
+ Date.now() - startTime,
381
+ process.memoryUsage().heapUsed / 1024 / 1024
382
+ );
383
+ }
384
+ } catch (e) {
385
+ }
386
+ }
387
+ const tsconfigs = await projectManager.getProjectsForPath(safeRoot);
388
+ const symbols = {};
389
+ const fileHashes = {};
390
+ for (const config of tsconfigs) {
391
+ const project = projectManager.ensureProject(config);
392
+ project.addSourceFilesFromTsConfig(config);
393
+ for (const sourceFile of project.getSourceFiles()) {
394
+ const filePath = sourceFile.getFilePath();
395
+ try {
396
+ fileHashes[filePath] = import_fs2.default.statSync(filePath).mtimeMs;
397
+ } catch {
398
+ continue;
399
+ }
400
+ for (const [name, decls] of sourceFile.getExportedDeclarations()) {
401
+ for (const decl of decls) {
402
+ const entry = {
403
+ name,
404
+ kind: this.mapKind(decl),
405
+ file: filePath,
406
+ line: decl.getStartLineNumber(),
407
+ column: decl.getStart() - decl.getStartLinePos(),
408
+ exported: true
409
+ };
410
+ (symbols[name] ||= []).push(entry);
411
+ }
412
+ }
413
+ for (const fn of sourceFile.getFunctions()) {
414
+ const name = fn.getName();
415
+ if (name && !symbols[name]?.some(
416
+ (s) => s.file === filePath && s.line === fn.getStartLineNumber()
417
+ )) {
418
+ const entry = {
419
+ name,
420
+ kind: "function",
421
+ file: filePath,
422
+ line: fn.getStartLineNumber(),
423
+ column: fn.getStart() - fn.getStartLinePos(),
424
+ exported: false
425
+ };
426
+ (symbols[name] ||= []).push(entry);
427
+ }
428
+ }
429
+ for (const cls of sourceFile.getClasses()) {
430
+ const name = cls.getName();
431
+ if (name && !symbols[name]?.some(
432
+ (s) => s.file === filePath && s.line === cls.getStartLineNumber()
433
+ )) {
434
+ const entry = {
435
+ name,
436
+ kind: "class",
437
+ file: filePath,
438
+ line: cls.getStartLineNumber(),
439
+ column: cls.getStart() - cls.getStartLinePos(),
440
+ exported: false
441
+ };
442
+ (symbols[name] ||= []).push(entry);
443
+ }
444
+ }
445
+ }
446
+ }
447
+ const cache = {
448
+ version: 1,
449
+ rootDir: safeRoot,
450
+ builtAt: (/* @__PURE__ */ new Date()).toISOString(),
451
+ fileHashes,
452
+ symbols
453
+ };
454
+ import_fs2.default.mkdirSync(import_path3.default.dirname(cachePath), { recursive: true });
455
+ import_fs2.default.writeFileSync(cachePath, JSON.stringify(cache));
456
+ this.index = symbols;
457
+ const duration = Date.now() - startTime;
458
+ const memoryUsage = process.memoryUsage().heapUsed / 1024 / 1024;
459
+ return this.computeStats(cache, duration, Math.round(memoryUsage));
460
+ }
461
+ lookup(name) {
462
+ return this.index[name] || [];
463
+ }
464
+ lookupByFile(file) {
465
+ return Object.values(this.index).flat().filter((e) => e.file === file);
466
+ }
467
+ };
468
+ var symbolIndex = new SymbolIndex();
469
+
470
+ // src/adapters/typescript-adapter.ts
471
+ init_security();
472
+ var TypeScriptAdapter = class {
473
+ async resolveDefinition(symbolName, path4) {
474
+ validateWorkspacePath(path4);
475
+ const indexHits = symbolIndex.lookup(symbolName);
476
+ if (indexHits.length > 0) {
477
+ const results = [];
478
+ for (const hit of indexHits) {
479
+ const tsconfig2 = await projectManager.findNearestTsConfig(hit.file);
480
+ if (tsconfig2) {
481
+ const project2 = projectManager.ensureProject(tsconfig2);
482
+ const sourceFile2 = project2.addSourceFileAtPathIfExists(hit.file);
483
+ if (sourceFile2) {
484
+ const exported2 = sourceFile2.getExportedDeclarations().get(symbolName);
485
+ if (exported2 && exported2.length > 0) {
486
+ results.push(this.mapToDefinitionLocation(exported2[0]));
487
+ continue;
488
+ }
489
+ }
490
+ }
491
+ results.push({
492
+ file: hit.file,
493
+ line: hit.line,
494
+ column: hit.column,
495
+ kind: hit.kind,
496
+ snippet: "",
497
+ documentation: void 0
498
+ });
499
+ }
500
+ return results;
501
+ }
502
+ if (import_fs3.default.statSync(path4).isDirectory()) {
503
+ return [];
504
+ }
505
+ const tsconfig = await projectManager.findNearestTsConfig(path4);
506
+ if (!tsconfig) return [];
507
+ const project = projectManager.ensureProject(tsconfig);
508
+ const sourceFile = project.addSourceFileAtPathIfExists(path4);
509
+ if (!sourceFile) return [];
510
+ const exported = sourceFile.getExportedDeclarations().get(symbolName);
511
+ if (!exported) return [];
512
+ return exported.map((decl) => this.mapToDefinitionLocation(decl));
513
+ }
514
+ async findReferences(symbolName, path4, limit = 50, offset = 0) {
515
+ validateWorkspacePath(path4);
516
+ const hits = symbolIndex.lookup(symbolName);
517
+ if (hits.length === 0) return { references: [], total_count: 0 };
518
+ const hit = hits[0];
519
+ const tsconfig = await projectManager.findNearestTsConfig(hit.file);
520
+ if (!tsconfig) return { references: [], total_count: 0 };
521
+ const project = projectManager.ensureProject(tsconfig);
522
+ const sourceFile = project.addSourceFileAtPathIfExists(hit.file);
523
+ if (!sourceFile) return { references: [], total_count: 0 };
524
+ const exported = sourceFile.getExportedDeclarations().get(symbolName);
525
+ if (!exported || exported.length === 0)
526
+ return { references: [], total_count: 0 };
527
+ const targetNode = exported[0];
528
+ try {
529
+ const { searchCode: searchCode2 } = await Promise.resolve().then(() => (init_search_code(), search_code_exports));
530
+ const searchResults = await searchCode2(
531
+ symbolName,
532
+ path4,
533
+ "*.{ts,tsx,js,jsx}",
534
+ 1e3,
535
+ false
536
+ );
537
+ const filesToLoad = [...new Set(searchResults.map((r) => r.file))];
538
+ console.log("Search code files to load:", filesToLoad);
539
+ for (const file of filesToLoad) {
540
+ project.addSourceFileAtPathIfExists(file);
541
+ }
542
+ } catch (_e) {
543
+ }
544
+ const refSymbols = targetNode.findReferences?.();
545
+ if (!refSymbols) return { references: [], total_count: 0 };
546
+ const results = [];
547
+ for (const refSymbol of refSymbols) {
548
+ for (const ref of refSymbol.getReferences()) {
549
+ const sf = ref.getSourceFile();
550
+ const lc = sf.getLineAndColumnAtPos(ref.getTextSpan().getStart());
551
+ results.push({
552
+ file: sf.getFilePath(),
553
+ line: lc.line,
554
+ column: lc.column,
555
+ text: ref.getNode().getParent()?.getText() || ref.getNode().getText()
556
+ });
557
+ }
558
+ }
559
+ const unique = this.deduplicateLocations(results);
560
+ return {
561
+ references: unique.slice(offset, offset + limit),
562
+ total_count: unique.length
563
+ };
564
+ }
565
+ async findImplementations(symbolName, path4, limit = 50, offset = 0) {
566
+ validateWorkspacePath(path4);
567
+ const hits = symbolIndex.lookup(symbolName);
568
+ if (hits.length === 0) return { implementations: [], total_count: 0 };
569
+ const hit = hits[0];
570
+ const tsconfig = await projectManager.findNearestTsConfig(hit.file);
571
+ if (!tsconfig) return { implementations: [], total_count: 0 };
572
+ const project = projectManager.ensureProject(tsconfig);
573
+ const sourceFile = project.addSourceFileAtPathIfExists(hit.file);
574
+ if (!sourceFile) return { implementations: [], total_count: 0 };
575
+ const exported = sourceFile.getExportedDeclarations().get(symbolName);
576
+ if (!exported || exported.length === 0)
577
+ return { implementations: [], total_count: 0 };
578
+ const targetNode = exported[0];
579
+ if (!import_ts_morph3.Node.isClassDeclaration(targetNode) && !import_ts_morph3.Node.isInterfaceDeclaration(targetNode)) {
580
+ return { implementations: [], total_count: 0 };
581
+ }
582
+ try {
583
+ const { searchCode: searchCode2 } = await Promise.resolve().then(() => (init_search_code(), search_code_exports));
584
+ const searchResults = await searchCode2(
585
+ symbolName,
586
+ path4,
587
+ "*.{ts,tsx,js,jsx}",
588
+ 1e3,
589
+ false
590
+ );
591
+ const filesToLoad = [...new Set(searchResults.map((r) => r.file))];
592
+ for (const file of filesToLoad) {
593
+ project.addSourceFileAtPathIfExists(file);
594
+ }
595
+ } catch (_e) {
596
+ }
597
+ const results = [];
598
+ const implementations = targetNode.getImplementations?.();
599
+ if (implementations) {
600
+ for (const impl of implementations) {
601
+ const sf = impl.getSourceFile();
602
+ const lc = sf.getLineAndColumnAtPos(impl.getTextSpan().getStart());
603
+ results.push({
604
+ file: sf.getFilePath(),
605
+ line: lc.line,
606
+ column: lc.column,
607
+ text: impl.getNode().getParent()?.getText() || impl.getNode().getText()
608
+ });
609
+ }
610
+ }
611
+ const unique = this.deduplicateLocations(results);
612
+ return {
613
+ implementations: unique.slice(offset, offset + limit),
614
+ total_count: unique.length
615
+ };
616
+ }
617
+ async getFileStructure(filePath) {
618
+ const safePath = validateWorkspacePath(filePath);
619
+ const tsconfig = await projectManager.findNearestTsConfig(safePath);
620
+ if (!tsconfig) return void 0;
621
+ const project = projectManager.ensureProject(tsconfig);
622
+ const sourceFile = project.addSourceFileAtPathIfExists(safePath);
623
+ if (!sourceFile) return void 0;
624
+ const structure = {
625
+ file: safePath,
626
+ imports: sourceFile.getImportDeclarations().map((imp) => ({
627
+ module: imp.getModuleSpecifierValue(),
628
+ names: imp.getNamedImports().map((ni) => ni.getName())
629
+ })),
630
+ exports: sourceFile.getExportSymbols().map((sym) => ({
631
+ name: sym.getName(),
632
+ kind: this.mapSymbolKind(sym)
633
+ })),
634
+ classes: sourceFile.getClasses().map((cls) => this.mapToClassInfo(cls)),
635
+ functions: sourceFile.getFunctions().map((fn) => this.mapToFunctionInfo(fn)),
636
+ interfaces: sourceFile.getInterfaces().map((itf) => this.mapToInterfaceInfo(itf)),
637
+ typeAliases: sourceFile.getTypeAliases().map((ta) => this.mapToTypeAliasInfo(ta)),
638
+ enums: sourceFile.getEnums().map((enm) => this.mapToEnumInfo(enm))
639
+ };
640
+ return structure;
641
+ }
642
+ mapToDefinitionLocation(node) {
643
+ const sourceFile = node.getSourceFile();
644
+ const lineAndColumn = sourceFile.getLineAndColumnAtPos(node.getStart());
645
+ return {
646
+ file: sourceFile.getFilePath(),
647
+ line: lineAndColumn.line,
648
+ column: lineAndColumn.column,
649
+ kind: this.mapNodeToSymbolKind(node),
650
+ snippet: node.getText(),
651
+ documentation: this.getJsDoc(node)
652
+ };
653
+ }
654
+ mapNodeToSymbolKind(node) {
655
+ if (import_ts_morph3.Node.isClassDeclaration(node)) return "class";
656
+ if (import_ts_morph3.Node.isFunctionDeclaration(node)) return "function";
657
+ if (import_ts_morph3.Node.isInterfaceDeclaration(node)) return "interface";
658
+ if (import_ts_morph3.Node.isTypeAliasDeclaration(node)) return "type_alias";
659
+ if (import_ts_morph3.Node.isEnumDeclaration(node)) return "enum";
660
+ if (import_ts_morph3.Node.isVariableDeclaration(node)) return "variable";
661
+ if (import_ts_morph3.Node.isMethodDeclaration(node)) return "method";
662
+ if (import_ts_morph3.Node.isPropertyDeclaration(node)) return "property";
663
+ if (import_ts_morph3.Node.isParameterDeclaration(node)) return "parameter";
664
+ return "variable";
665
+ }
666
+ mapSymbolKind(symbol) {
667
+ const decls = symbol.getDeclarations();
668
+ if (decls.length > 0) return this.mapNodeToSymbolKind(decls[0]);
669
+ return "variable";
670
+ }
671
+ getJsDoc(node) {
672
+ if (import_ts_morph3.Node.isJSDocable(node)) {
673
+ const docs = node.getJsDocs();
674
+ if (docs.length > 0) {
675
+ return docs[0].getCommentText();
676
+ }
677
+ }
678
+ return void 0;
679
+ }
680
+ getSymbolDocs(node) {
681
+ if (import_ts_morph3.Node.isJSDocable(node)) {
682
+ const docs = node.getJsDocs();
683
+ if (docs.length > 0) {
684
+ const doc = docs[0];
685
+ return {
686
+ documentation: doc.getCommentText(),
687
+ tags: doc.getTags().map((tag) => ({
688
+ name: tag.getTagName(),
689
+ text: tag.getCommentText() || ""
690
+ }))
691
+ };
692
+ }
693
+ }
694
+ return void 0;
695
+ }
696
+ mapToClassInfo(cls) {
697
+ return {
698
+ name: cls.getName() || "anonymous",
699
+ ...this.getSymbolDocs(cls),
700
+ methods: cls.getMethods().map((m) => this.mapToFunctionInfo(m)),
701
+ properties: cls.getProperties().map((p) => this.mapToPropertyInfo(p))
702
+ };
703
+ }
704
+ mapToFunctionInfo(fn) {
705
+ return {
706
+ name: fn.getName() || "anonymous",
707
+ ...this.getSymbolDocs(fn),
708
+ params: fn.getParameters().map((p) => ({
709
+ name: p.getName(),
710
+ type: p.getType().getText()
711
+ })),
712
+ returnType: fn.getReturnType().getText()
713
+ };
714
+ }
715
+ mapToPropertyInfo(p) {
716
+ return {
717
+ name: p.getName(),
718
+ type: p.getType().getText(),
719
+ ...this.getSymbolDocs(p)
720
+ };
721
+ }
722
+ mapToInterfaceInfo(itf) {
723
+ return {
724
+ name: itf.getName(),
725
+ ...this.getSymbolDocs(itf),
726
+ properties: itf.getProperties().map((p) => this.mapToPropertyInfo(p)),
727
+ methods: itf.getMethods().map((m) => this.mapToFunctionInfo(m))
728
+ };
729
+ }
730
+ mapToTypeAliasInfo(ta) {
731
+ return {
732
+ name: ta.getName(),
733
+ type: ta.getType().getText(),
734
+ ...this.getSymbolDocs(ta)
735
+ };
736
+ }
737
+ mapToEnumInfo(enm) {
738
+ return {
739
+ name: enm.getName(),
740
+ ...this.getSymbolDocs(enm),
741
+ members: enm.getMembers().map((m) => m.getName())
742
+ };
743
+ }
744
+ deduplicateLocations(locations) {
745
+ const seen = /* @__PURE__ */ new Set();
746
+ return locations.filter((loc) => {
747
+ const key = `${loc.file}:${loc.line}:${loc.column}`;
748
+ if (seen.has(key)) return false;
749
+ seen.add(key);
750
+ return true;
751
+ });
752
+ }
753
+ };
754
+ var typescriptAdapter = new TypeScriptAdapter();
755
+
756
+ // src/tools/resolve-definition.ts
757
+ async function resolveDefinition(symbol, path4) {
758
+ return await typescriptAdapter.resolveDefinition(symbol, path4);
759
+ }
760
+
761
+ // src/tools/find-references.ts
762
+ init_cjs_shims();
763
+ async function findReferences(symbol, path4, limit = 50, offset = 0) {
764
+ return await typescriptAdapter.findReferences(symbol, path4, limit, offset);
765
+ }
766
+
767
+ // src/tools/find-implementations.ts
768
+ init_cjs_shims();
769
+ async function findImplementations(symbol, path4, limit = 50, offset = 0) {
770
+ return await typescriptAdapter.findImplementations(
771
+ symbol,
772
+ path4,
773
+ limit,
774
+ offset
775
+ );
776
+ }
777
+
778
+ // src/tools/get-file-structure.ts
779
+ init_cjs_shims();
780
+ async function getFileStructure(file) {
781
+ return await typescriptAdapter.getFileStructure(file);
782
+ }
783
+
784
+ // src/index.ts
785
+ init_search_code();
786
+
787
+ // src/tools/get-symbol-docs.ts
788
+ init_cjs_shims();
789
+ var import_ts_morph4 = require("ts-morph");
790
+ init_security();
791
+ async function getSymbolDocs(symbol, filePath) {
792
+ const safePath = validateWorkspacePath(filePath);
793
+ const tsconfig = await projectManager.findNearestTsConfig(safePath);
794
+ if (!tsconfig) return void 0;
795
+ const project = projectManager.ensureProject(tsconfig);
796
+ const sourceFile = project.addSourceFileAtPathIfExists(safePath);
797
+ if (sourceFile) {
798
+ const node = sourceFile.getDescendantsOfKind(import_ts_morph4.SyntaxKind.Identifier).find((id) => id.getText() === symbol);
799
+ if (node) {
800
+ const decls = node.getSymbol()?.getDeclarations();
801
+ if (decls && decls.length > 0) {
802
+ const docs = typescriptAdapter.getSymbolDocs(decls[0]);
803
+ if (docs) {
804
+ return {
805
+ symbol,
806
+ file: sourceFile.getFilePath(),
807
+ line: sourceFile.getLineAndColumnAtPos(decls[0].getStart()).line,
808
+ ...docs
809
+ };
810
+ }
811
+ }
812
+ }
813
+ }
814
+ return void 0;
815
+ }
816
+
817
+ // src/tools/build-symbol-index.ts
818
+ init_cjs_shims();
819
+ async function buildSymbolIndex(path4) {
820
+ return await symbolIndex.buildIndex(path4);
821
+ }
822
+
823
+ // src/index.ts
824
+ var ASTExplorerServer = class {
825
+ server;
826
+ version = "0.1.0";
827
+ constructor() {
828
+ this.server = new import_server.Server(
829
+ {
830
+ name: "ast-explorer-server",
831
+ version: this.version
832
+ },
833
+ {
834
+ capabilities: {
835
+ tools: {}
836
+ }
837
+ }
838
+ );
839
+ this.setupHandlers();
840
+ this.server.onerror = (error) => {
841
+ console.error("[MCP Error]", error);
842
+ };
843
+ }
844
+ setupHandlers() {
845
+ this.server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
846
+ return {
847
+ tools: [
848
+ {
849
+ name: "resolve_definition",
850
+ description: "Find where a symbol is defined using TypeScript AST.",
851
+ inputSchema: {
852
+ type: "object",
853
+ properties: {
854
+ symbol: {
855
+ type: "string",
856
+ description: "Symbol name (e.g., function, class)"
857
+ },
858
+ path: {
859
+ type: "string",
860
+ description: "Project root or target directory"
861
+ }
862
+ },
863
+ required: ["symbol", "path"]
864
+ }
865
+ },
866
+ {
867
+ name: "find_references",
868
+ description: "Find all usages of a symbol across the project.",
869
+ inputSchema: {
870
+ type: "object",
871
+ properties: {
872
+ symbol: { type: "string", description: "Symbol name" },
873
+ path: { type: "string", description: "Project root" },
874
+ limit: { type: "number", default: 50 },
875
+ offset: { type: "number", default: 0 }
876
+ },
877
+ required: ["symbol", "path"]
878
+ }
879
+ },
880
+ {
881
+ name: "find_implementations",
882
+ description: "Find implementations of interfaces or abstract classes.",
883
+ inputSchema: {
884
+ type: "object",
885
+ properties: {
886
+ symbol: { type: "string", description: "Interface/Class name" },
887
+ path: { type: "string", description: "Project root" },
888
+ limit: { type: "number", default: 50 },
889
+ offset: { type: "number", default: 0 }
890
+ },
891
+ required: ["symbol", "path"]
892
+ }
893
+ },
894
+ {
895
+ name: "get_file_structure",
896
+ description: "Get structural overview of a file (imports, exports, symbols).",
897
+ inputSchema: {
898
+ type: "object",
899
+ properties: {
900
+ file: { type: "string", description: "Absolute path to file" }
901
+ },
902
+ required: ["file"]
903
+ }
904
+ },
905
+ {
906
+ name: "search_code",
907
+ description: "Fast regex search via bundled ripgrep.",
908
+ inputSchema: {
909
+ type: "object",
910
+ properties: {
911
+ pattern: { type: "string", description: "Search pattern" },
912
+ path: { type: "string", description: "Directory to search" },
913
+ filePattern: { type: "string", description: "Glob filter" },
914
+ limit: { type: "number", default: 50 },
915
+ regex: { type: "boolean", default: true }
916
+ },
917
+ required: ["pattern", "path"]
918
+ }
919
+ },
920
+ {
921
+ name: "get_symbol_docs",
922
+ description: "Get JSDoc/TSDoc for a specific symbol.",
923
+ inputSchema: {
924
+ type: "object",
925
+ properties: {
926
+ symbol: { type: "string", description: "Symbol name" },
927
+ path: { type: "string", description: "Project root" }
928
+ },
929
+ required: ["symbol", "path"]
930
+ }
931
+ },
932
+ {
933
+ name: "build_symbol_index",
934
+ description: "Warm the symbol index for faster navigation.",
935
+ inputSchema: {
936
+ type: "object",
937
+ properties: {
938
+ path: { type: "string", description: "Project root to index" }
939
+ },
940
+ required: ["path"]
941
+ }
942
+ }
943
+ ]
944
+ };
945
+ });
946
+ this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
947
+ const { name, arguments: args } = request.params;
948
+ try {
949
+ switch (name) {
950
+ case "resolve_definition": {
951
+ const { symbol, path: path4 } = ResolveDefinitionSchema.parse(args);
952
+ const results = await resolveDefinition(symbol, path4);
953
+ return {
954
+ content: [
955
+ { type: "text", text: JSON.stringify(results, null, 2) }
956
+ ]
957
+ };
958
+ }
959
+ case "find_references": {
960
+ const { symbol, path: path4, limit, offset } = FindReferencesSchema.parse(args);
961
+ const results = await findReferences(symbol, path4, limit, offset);
962
+ return {
963
+ content: [
964
+ { type: "text", text: JSON.stringify(results, null, 2) }
965
+ ]
966
+ };
967
+ }
968
+ case "find_implementations": {
969
+ const { symbol, path: path4, limit, offset } = FindImplementationsSchema.parse(args);
970
+ const results = await findImplementations(
971
+ symbol,
972
+ path4,
973
+ limit,
974
+ offset
975
+ );
976
+ return {
977
+ content: [
978
+ { type: "text", text: JSON.stringify(results, null, 2) }
979
+ ]
980
+ };
981
+ }
982
+ case "get_file_structure": {
983
+ const { file } = GetFileStructureSchema.parse(args);
984
+ const structure = await getFileStructure(file);
985
+ return {
986
+ content: [
987
+ { type: "text", text: JSON.stringify(structure, null, 2) }
988
+ ]
989
+ };
990
+ }
991
+ case "search_code": {
992
+ const { pattern, path: path4, filePattern, limit, regex } = SearchCodeSchema.parse(args);
993
+ const results = await searchCode(
994
+ pattern,
995
+ path4,
996
+ filePattern,
997
+ limit,
998
+ regex
999
+ );
1000
+ return {
1001
+ content: [
1002
+ { type: "text", text: JSON.stringify(results, null, 2) }
1003
+ ]
1004
+ };
1005
+ }
1006
+ case "get_symbol_docs": {
1007
+ const { symbol, path: path4 } = GetSymbolDocsSchema.parse(args);
1008
+ const docs = await getSymbolDocs(symbol, path4);
1009
+ return {
1010
+ content: [{ type: "text", text: JSON.stringify(docs, null, 2) }]
1011
+ };
1012
+ }
1013
+ case "build_symbol_index": {
1014
+ const { path: path4 } = BuildSymbolIndexSchema.parse(args);
1015
+ const stats = await buildSymbolIndex(path4);
1016
+ return {
1017
+ content: [{ type: "text", text: JSON.stringify(stats, null, 2) }]
1018
+ };
1019
+ }
1020
+ default:
1021
+ throw new Error(`Unknown tool: ${name}`);
1022
+ }
1023
+ } catch (error) {
1024
+ return {
1025
+ content: [{ type: "text", text: `Error: ${error.message}` }],
1026
+ isError: true
1027
+ };
1028
+ }
1029
+ });
1030
+ }
1031
+ async run() {
1032
+ const transport = new import_stdio.StdioServerTransport();
1033
+ await this.server.connect(transport);
1034
+ console.error("AST Explorer MCP Server started");
1035
+ }
1036
+ };
1037
+ var server = new ASTExplorerServer();
1038
+ server.run().catch((error) => {
1039
+ console.error("Fatal error starting AST Explorer MCP Server:", error);
1040
+ process.exit(1);
1041
+ });
1042
+ // Annotate the CommonJS export names for ESM import in node:
1043
+ 0 && (module.exports = {
1044
+ ASTExplorerServer
1045
+ });
1046
+ //# sourceMappingURL=index.cjs.map