@aiready/ast-mcp-server 0.1.0 → 0.1.1
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 +771 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +742 -0
- package/dist/index.js.map +1 -0
- package/package.json +1 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,771 @@
|
|
|
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 __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ASTExplorerServer: () => ASTExplorerServer
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
37
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
38
|
+
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
39
|
+
|
|
40
|
+
// src/schemas.ts
|
|
41
|
+
var import_zod = require("zod");
|
|
42
|
+
var ResolveDefinitionSchema = import_zod.z.object({
|
|
43
|
+
symbol: import_zod.z.string().describe(
|
|
44
|
+
"Symbol name to resolve (function, class, type, interface, etc.)"
|
|
45
|
+
),
|
|
46
|
+
path: import_zod.z.string().describe("Project root or target directory")
|
|
47
|
+
});
|
|
48
|
+
var FindReferencesSchema = import_zod.z.object({
|
|
49
|
+
symbol: import_zod.z.string().describe("Symbol name to find references for"),
|
|
50
|
+
path: import_zod.z.string().describe("Project root directory"),
|
|
51
|
+
limit: import_zod.z.number().optional().default(50).describe("Max results per page (default 50)"),
|
|
52
|
+
offset: import_zod.z.number().optional().default(0).describe("Pagination offset")
|
|
53
|
+
});
|
|
54
|
+
var FindImplementationsSchema = import_zod.z.object({
|
|
55
|
+
symbol: import_zod.z.string().describe("Interface or abstract class name to find implementations for"),
|
|
56
|
+
path: import_zod.z.string().describe("Project root directory"),
|
|
57
|
+
limit: import_zod.z.number().optional().default(50).describe("Max results per page"),
|
|
58
|
+
offset: import_zod.z.number().optional().default(0).describe("Pagination offset")
|
|
59
|
+
});
|
|
60
|
+
var GetFileStructureSchema = import_zod.z.object({
|
|
61
|
+
file: import_zod.z.string().describe("Absolute path to the file to analyze")
|
|
62
|
+
});
|
|
63
|
+
var SearchCodeSchema = import_zod.z.object({
|
|
64
|
+
pattern: import_zod.z.string().describe("Search pattern (regex)"),
|
|
65
|
+
path: import_zod.z.string().describe("Directory to search in"),
|
|
66
|
+
filePattern: import_zod.z.string().optional().describe('Glob filter (e.g., "*.ts")'),
|
|
67
|
+
limit: import_zod.z.number().optional().default(50).describe("Max matches to return")
|
|
68
|
+
});
|
|
69
|
+
var GetSymbolDocsSchema = import_zod.z.object({
|
|
70
|
+
symbol: import_zod.z.string().describe("Symbol name to get documentation for"),
|
|
71
|
+
path: import_zod.z.string().describe("Project root directory")
|
|
72
|
+
});
|
|
73
|
+
var BuildSymbolIndexSchema = import_zod.z.object({
|
|
74
|
+
path: import_zod.z.string().describe("Project root directory to index")
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// src/adapters/typescript-adapter.ts
|
|
78
|
+
var import_ts_morph2 = require("ts-morph");
|
|
79
|
+
|
|
80
|
+
// src/project-manager.ts
|
|
81
|
+
var import_ts_morph = require("ts-morph");
|
|
82
|
+
var import_path = __toESM(require("path"), 1);
|
|
83
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
84
|
+
var import_glob = require("glob");
|
|
85
|
+
var ProjectManager = class {
|
|
86
|
+
projects = /* @__PURE__ */ new Map();
|
|
87
|
+
tsconfigCache = /* @__PURE__ */ new Map();
|
|
88
|
+
/**
|
|
89
|
+
* Find all tsconfig.json files in a directory (recursive)
|
|
90
|
+
*/
|
|
91
|
+
async findTsConfigs(rootDir) {
|
|
92
|
+
if (this.tsconfigCache.has(rootDir)) {
|
|
93
|
+
return this.tsconfigCache.get(rootDir);
|
|
94
|
+
}
|
|
95
|
+
const configs = await (0, import_glob.glob)("**/tsconfig.json", {
|
|
96
|
+
cwd: rootDir,
|
|
97
|
+
absolute: true,
|
|
98
|
+
ignore: ["**/node_modules/**", "**/dist/**"]
|
|
99
|
+
});
|
|
100
|
+
this.tsconfigCache.set(rootDir, configs);
|
|
101
|
+
return configs;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get or create a Project for a specific file path
|
|
105
|
+
*/
|
|
106
|
+
async getProjectForFile(filePath) {
|
|
107
|
+
const tsconfigPath = await this.findNearestTsConfig(filePath);
|
|
108
|
+
if (!tsconfigPath) return void 0;
|
|
109
|
+
return this.getOrCreateProject(tsconfigPath);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get or create a Project for a tsconfig path
|
|
113
|
+
*/
|
|
114
|
+
getOrCreateProject(tsconfigPath) {
|
|
115
|
+
if (this.projects.has(tsconfigPath)) {
|
|
116
|
+
return this.projects.get(tsconfigPath);
|
|
117
|
+
}
|
|
118
|
+
const project = new import_ts_morph.Project({
|
|
119
|
+
tsConfigFilePath: tsconfigPath,
|
|
120
|
+
skipAddingFilesFromTsConfig: false
|
|
121
|
+
});
|
|
122
|
+
this.projects.set(tsconfigPath, project);
|
|
123
|
+
return project;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Find the nearest tsconfig.json for a file
|
|
127
|
+
*/
|
|
128
|
+
async findNearestTsConfig(filePath) {
|
|
129
|
+
let currentDir = import_path.default.dirname(filePath);
|
|
130
|
+
const root = import_path.default.parse(currentDir).root;
|
|
131
|
+
while (currentDir !== root) {
|
|
132
|
+
const tsconfigPath = import_path.default.join(currentDir, "tsconfig.json");
|
|
133
|
+
if (import_fs.default.existsSync(tsconfigPath)) {
|
|
134
|
+
return tsconfigPath;
|
|
135
|
+
}
|
|
136
|
+
currentDir = import_path.default.dirname(currentDir);
|
|
137
|
+
}
|
|
138
|
+
return void 0;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get all projects that might contain a file or serve a path
|
|
142
|
+
*/
|
|
143
|
+
async getProjectsForPath(rootDir) {
|
|
144
|
+
const configs = await this.findTsConfigs(rootDir);
|
|
145
|
+
return configs.map((config) => this.getOrCreateProject(config));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Dispose all projects to free memory
|
|
149
|
+
*/
|
|
150
|
+
disposeAll() {
|
|
151
|
+
for (const project of this.projects.values()) {
|
|
152
|
+
project.getLanguageService().compilerObject.dispose();
|
|
153
|
+
}
|
|
154
|
+
this.projects.clear();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var projectManager = new ProjectManager();
|
|
158
|
+
|
|
159
|
+
// src/adapters/typescript-adapter.ts
|
|
160
|
+
var TypeScriptAdapter = class {
|
|
161
|
+
/**
|
|
162
|
+
* Resolve definition of a symbol at a path
|
|
163
|
+
*/
|
|
164
|
+
async resolveDefinition(symbolName, path2) {
|
|
165
|
+
const projects = await projectManager.getProjectsForPath(path2);
|
|
166
|
+
const results = [];
|
|
167
|
+
for (const project of projects) {
|
|
168
|
+
const sourceFiles = project.getSourceFiles();
|
|
169
|
+
for (const sourceFile of sourceFiles) {
|
|
170
|
+
const nodes = sourceFile.getDescendantsOfKind(import_ts_morph2.SyntaxKind.Identifier).filter((id) => id.getText() === symbolName);
|
|
171
|
+
for (const node of nodes) {
|
|
172
|
+
const definitions = node.getDefinitionNodes();
|
|
173
|
+
for (const defNode of definitions) {
|
|
174
|
+
results.push(this.mapToDefinitionLocation(defNode));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return this.deduplicateLocations(results);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Find references to a symbol
|
|
183
|
+
*/
|
|
184
|
+
async findReferences(symbolName, path2, limit = 50, offset = 0) {
|
|
185
|
+
const projects = await projectManager.getProjectsForPath(path2);
|
|
186
|
+
const results = [];
|
|
187
|
+
for (const project of projects) {
|
|
188
|
+
const sourceFiles = project.getSourceFiles();
|
|
189
|
+
for (const sourceFile of sourceFiles) {
|
|
190
|
+
const nodes = sourceFile.getDescendantsOfKind(import_ts_morph2.SyntaxKind.Identifier).filter((id) => id.getText() === symbolName);
|
|
191
|
+
for (const node of nodes) {
|
|
192
|
+
const referencedSymbols = node.findReferences();
|
|
193
|
+
for (const referencedSymbol of referencedSymbols) {
|
|
194
|
+
const references = referencedSymbol.getReferences();
|
|
195
|
+
for (const ref of references) {
|
|
196
|
+
const sourceFile2 = ref.getSourceFile();
|
|
197
|
+
const lineAndColumn = sourceFile2.getLineAndColumnAtPos(
|
|
198
|
+
ref.getTextSpan().getStart()
|
|
199
|
+
);
|
|
200
|
+
results.push({
|
|
201
|
+
file: sourceFile2.getFilePath(),
|
|
202
|
+
line: lineAndColumn.line,
|
|
203
|
+
column: lineAndColumn.column,
|
|
204
|
+
text: ref.getNode().getParent()?.getText() || ref.getNode().getText()
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const uniqueResults = this.deduplicateLocations(results);
|
|
212
|
+
return {
|
|
213
|
+
references: uniqueResults.slice(offset, offset + limit),
|
|
214
|
+
total_count: uniqueResults.length
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Find implementations for a symbol
|
|
219
|
+
*/
|
|
220
|
+
async findImplementations(symbolName, path2, limit = 50, offset = 0) {
|
|
221
|
+
const projects = await projectManager.getProjectsForPath(path2);
|
|
222
|
+
const results = [];
|
|
223
|
+
for (const project of projects) {
|
|
224
|
+
const sourceFiles = project.getSourceFiles();
|
|
225
|
+
for (const sourceFile of sourceFiles) {
|
|
226
|
+
const nodes = sourceFile.getDescendantsOfKind(import_ts_morph2.SyntaxKind.Identifier).filter((id) => id.getText() === symbolName);
|
|
227
|
+
for (const node of nodes) {
|
|
228
|
+
const implementations = node.getImplementations();
|
|
229
|
+
for (const impl of implementations) {
|
|
230
|
+
const sourceFile2 = impl.getSourceFile();
|
|
231
|
+
const lineAndColumn = sourceFile2.getLineAndColumnAtPos(
|
|
232
|
+
impl.getTextSpan().getStart()
|
|
233
|
+
);
|
|
234
|
+
results.push({
|
|
235
|
+
file: sourceFile2.getFilePath(),
|
|
236
|
+
line: lineAndColumn.line,
|
|
237
|
+
column: lineAndColumn.column,
|
|
238
|
+
text: impl.getNode().getParent()?.getText() || impl.getNode().getText()
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const uniqueResults = this.deduplicateLocations(results);
|
|
245
|
+
return {
|
|
246
|
+
implementations: uniqueResults.slice(offset, offset + limit),
|
|
247
|
+
total_count: uniqueResults.length
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get file structure overview
|
|
252
|
+
*/
|
|
253
|
+
async getFileStructure(filePath) {
|
|
254
|
+
const project = await projectManager.getProjectForFile(filePath);
|
|
255
|
+
if (!project) return void 0;
|
|
256
|
+
const sourceFile = project.getSourceFile(filePath);
|
|
257
|
+
if (!sourceFile) return void 0;
|
|
258
|
+
const structure = {
|
|
259
|
+
file: filePath,
|
|
260
|
+
imports: sourceFile.getImportDeclarations().map((imp) => ({
|
|
261
|
+
module: imp.getModuleSpecifierValue(),
|
|
262
|
+
names: imp.getNamedImports().map((ni) => ni.getName())
|
|
263
|
+
})),
|
|
264
|
+
exports: sourceFile.getExportSymbols().map((sym) => ({
|
|
265
|
+
name: sym.getName(),
|
|
266
|
+
kind: this.mapSymbolKind(sym)
|
|
267
|
+
})),
|
|
268
|
+
classes: sourceFile.getClasses().map((cls) => this.mapToClassInfo(cls)),
|
|
269
|
+
functions: sourceFile.getFunctions().map((fn) => this.mapToFunctionInfo(fn)),
|
|
270
|
+
interfaces: sourceFile.getInterfaces().map((itf) => this.mapToInterfaceInfo(itf)),
|
|
271
|
+
typeAliases: sourceFile.getTypeAliases().map((ta) => this.mapToTypeAliasInfo(ta)),
|
|
272
|
+
enums: sourceFile.getEnums().map((enm) => this.mapToEnumInfo(enm))
|
|
273
|
+
};
|
|
274
|
+
return structure;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Helper: Map ts-morph Node (Declaration) to DefinitionLocation
|
|
278
|
+
*/
|
|
279
|
+
mapToDefinitionLocation(node) {
|
|
280
|
+
const sourceFile = node.getSourceFile();
|
|
281
|
+
const lineAndColumn = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
282
|
+
return {
|
|
283
|
+
file: sourceFile.getFilePath(),
|
|
284
|
+
line: lineAndColumn.line,
|
|
285
|
+
column: lineAndColumn.column,
|
|
286
|
+
kind: this.mapNodeToSymbolKind(node),
|
|
287
|
+
snippet: node.getText(),
|
|
288
|
+
documentation: this.getJsDoc(node)
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Helper: Map ts-morph Node to SymbolKind
|
|
293
|
+
*/
|
|
294
|
+
mapNodeToSymbolKind(node) {
|
|
295
|
+
if (import_ts_morph2.Node.isClassDeclaration(node)) return "class";
|
|
296
|
+
if (import_ts_morph2.Node.isFunctionDeclaration(node)) return "function";
|
|
297
|
+
if (import_ts_morph2.Node.isInterfaceDeclaration(node)) return "interface";
|
|
298
|
+
if (import_ts_morph2.Node.isTypeAliasDeclaration(node)) return "type_alias";
|
|
299
|
+
if (import_ts_morph2.Node.isEnumDeclaration(node)) return "enum";
|
|
300
|
+
if (import_ts_morph2.Node.isVariableDeclaration(node)) return "variable";
|
|
301
|
+
if (import_ts_morph2.Node.isMethodDeclaration(node)) return "method";
|
|
302
|
+
if (import_ts_morph2.Node.isPropertyDeclaration(node)) return "property";
|
|
303
|
+
if (import_ts_morph2.Node.isParameterDeclaration(node)) return "parameter";
|
|
304
|
+
return "variable";
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Helper: Map Symbol to SymbolKind
|
|
308
|
+
*/
|
|
309
|
+
mapSymbolKind(symbol) {
|
|
310
|
+
const decls = symbol.getDeclarations();
|
|
311
|
+
if (decls.length > 0) return this.mapNodeToSymbolKind(decls[0]);
|
|
312
|
+
return "variable";
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Helper: Get JSDoc from Node
|
|
316
|
+
*/
|
|
317
|
+
getJsDoc(node) {
|
|
318
|
+
if (import_ts_morph2.Node.isJSDocable(node)) {
|
|
319
|
+
const docs = node.getJsDocs();
|
|
320
|
+
if (docs.length > 0) {
|
|
321
|
+
return docs[0].getCommentText();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return void 0;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Helper: Get full JSDoc info (with tags)
|
|
328
|
+
*/
|
|
329
|
+
getSymbolDocs(node) {
|
|
330
|
+
if (import_ts_morph2.Node.isJSDocable(node)) {
|
|
331
|
+
const docs = node.getJsDocs();
|
|
332
|
+
if (docs.length > 0) {
|
|
333
|
+
const doc = docs[0];
|
|
334
|
+
return {
|
|
335
|
+
documentation: doc.getCommentText(),
|
|
336
|
+
tags: doc.getTags().map((tag) => ({
|
|
337
|
+
name: tag.getTagName(),
|
|
338
|
+
text: tag.getCommentText() || ""
|
|
339
|
+
}))
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return void 0;
|
|
344
|
+
}
|
|
345
|
+
mapToClassInfo(cls) {
|
|
346
|
+
return {
|
|
347
|
+
name: cls.getName() || "anonymous",
|
|
348
|
+
...this.getSymbolDocs(cls),
|
|
349
|
+
methods: cls.getMethods().map((m) => this.mapToFunctionInfo(m)),
|
|
350
|
+
properties: cls.getProperties().map((p) => this.mapToPropertyInfo(p))
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
mapToFunctionInfo(fn) {
|
|
354
|
+
return {
|
|
355
|
+
name: fn.getName() || "anonymous",
|
|
356
|
+
...this.getSymbolDocs(fn),
|
|
357
|
+
params: fn.getParameters().map((p) => ({
|
|
358
|
+
name: p.getName(),
|
|
359
|
+
type: p.getType().getText()
|
|
360
|
+
})),
|
|
361
|
+
returnType: fn.getReturnType().getText()
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
mapToPropertyInfo(p) {
|
|
365
|
+
return {
|
|
366
|
+
name: p.getName(),
|
|
367
|
+
type: p.getType().getText(),
|
|
368
|
+
...this.getSymbolDocs(p)
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
mapToInterfaceInfo(itf) {
|
|
372
|
+
return {
|
|
373
|
+
name: itf.getName(),
|
|
374
|
+
...this.getSymbolDocs(itf),
|
|
375
|
+
properties: itf.getProperties().map((p) => this.mapToPropertyInfo(p)),
|
|
376
|
+
methods: itf.getMethods().map((m) => this.mapToFunctionInfo(m))
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
mapToTypeAliasInfo(ta) {
|
|
380
|
+
return {
|
|
381
|
+
name: ta.getName(),
|
|
382
|
+
type: ta.getType().getText(),
|
|
383
|
+
...this.getSymbolDocs(ta)
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
mapToEnumInfo(enm) {
|
|
387
|
+
return {
|
|
388
|
+
name: enm.getName(),
|
|
389
|
+
...this.getSymbolDocs(enm),
|
|
390
|
+
members: enm.getMembers().map((m) => m.getName())
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Helper: Deduplicate locations
|
|
395
|
+
*/
|
|
396
|
+
deduplicateLocations(locations) {
|
|
397
|
+
const seen = /* @__PURE__ */ new Set();
|
|
398
|
+
return locations.filter((loc) => {
|
|
399
|
+
const key = `${loc.file}:${loc.line}:${loc.column}`;
|
|
400
|
+
if (seen.has(key)) return false;
|
|
401
|
+
seen.add(key);
|
|
402
|
+
return true;
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
var typescriptAdapter = new TypeScriptAdapter();
|
|
407
|
+
|
|
408
|
+
// src/tools/resolve-definition.ts
|
|
409
|
+
async function resolveDefinition(symbol, path2) {
|
|
410
|
+
return await typescriptAdapter.resolveDefinition(symbol, path2);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/tools/find-references.ts
|
|
414
|
+
async function findReferences(symbol, path2, limit = 50, offset = 0) {
|
|
415
|
+
return await typescriptAdapter.findReferences(symbol, path2, limit, offset);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// src/tools/find-implementations.ts
|
|
419
|
+
async function findImplementations(symbol, path2, limit = 50, offset = 0) {
|
|
420
|
+
return await typescriptAdapter.findImplementations(
|
|
421
|
+
symbol,
|
|
422
|
+
path2,
|
|
423
|
+
limit,
|
|
424
|
+
offset
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/tools/get-file-structure.ts
|
|
429
|
+
async function getFileStructure(file) {
|
|
430
|
+
return await typescriptAdapter.getFileStructure(file);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/tools/search-code.ts
|
|
434
|
+
var import_child_process = require("child_process");
|
|
435
|
+
var import_util = require("util");
|
|
436
|
+
var import_ripgrep = require("@vscode/ripgrep");
|
|
437
|
+
var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
|
|
438
|
+
async function searchCode(pattern, searchPath, filePattern, limit = 50) {
|
|
439
|
+
const args = [
|
|
440
|
+
"--json",
|
|
441
|
+
"--max-count",
|
|
442
|
+
limit.toString(),
|
|
443
|
+
"--fixed-strings",
|
|
444
|
+
// Default to fixed strings unless we want regex
|
|
445
|
+
pattern,
|
|
446
|
+
searchPath
|
|
447
|
+
];
|
|
448
|
+
if (filePattern) {
|
|
449
|
+
args.push("--glob", filePattern);
|
|
450
|
+
}
|
|
451
|
+
args.push("--glob", "!**/node_modules/**");
|
|
452
|
+
args.push("--glob", "!**/dist/**");
|
|
453
|
+
args.push("--glob", "!**/.git/**");
|
|
454
|
+
try {
|
|
455
|
+
const { stdout } = await execFileAsync(import_ripgrep.rgPath, args);
|
|
456
|
+
const lines = stdout.split("\n").filter(Boolean);
|
|
457
|
+
const results = [];
|
|
458
|
+
for (const line of lines) {
|
|
459
|
+
const data = JSON.parse(line);
|
|
460
|
+
if (data.type === "match") {
|
|
461
|
+
const file = data.data.path.text;
|
|
462
|
+
const lineNumber = data.data.line_number;
|
|
463
|
+
const submatches = data.data.submatches;
|
|
464
|
+
for (const submatch of submatches) {
|
|
465
|
+
results.push({
|
|
466
|
+
file,
|
|
467
|
+
line: lineNumber,
|
|
468
|
+
column: submatch.start,
|
|
469
|
+
text: data.data.lines.text.trim()
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return results.slice(0, limit);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
if (error.code === 1) {
|
|
477
|
+
return [];
|
|
478
|
+
}
|
|
479
|
+
throw error;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// src/tools/get-symbol-docs.ts
|
|
484
|
+
var import_ts_morph3 = require("ts-morph");
|
|
485
|
+
async function getSymbolDocs(symbol, filePath) {
|
|
486
|
+
const projects = await projectManager.getProjectsForPath(filePath);
|
|
487
|
+
for (const project of projects) {
|
|
488
|
+
const sourceFile = project.getSourceFile(filePath);
|
|
489
|
+
if (sourceFile) {
|
|
490
|
+
const node = sourceFile.getDescendantsOfKind(import_ts_morph3.SyntaxKind.Identifier).find((id) => id.getText() === symbol);
|
|
491
|
+
if (node) {
|
|
492
|
+
const decls = node.getSymbol()?.getDeclarations();
|
|
493
|
+
if (decls && decls.length > 0) {
|
|
494
|
+
const docs = typescriptAdapter.getSymbolDocs(decls[0]);
|
|
495
|
+
if (docs) {
|
|
496
|
+
return {
|
|
497
|
+
symbol,
|
|
498
|
+
file: sourceFile.getFilePath(),
|
|
499
|
+
line: sourceFile.getLineAndColumnAtPos(decls[0].getStart()).line,
|
|
500
|
+
...docs
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return void 0;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/index/symbol-index.ts
|
|
511
|
+
var SymbolIndex = class {
|
|
512
|
+
/**
|
|
513
|
+
* Build/Warm the index for a given path
|
|
514
|
+
*/
|
|
515
|
+
async buildIndex(rootDir) {
|
|
516
|
+
const startTime = Date.now();
|
|
517
|
+
const projects = await projectManager.getProjectsForPath(rootDir);
|
|
518
|
+
let fileCount = 0;
|
|
519
|
+
let functionCount = 0;
|
|
520
|
+
let classCount = 0;
|
|
521
|
+
let interfaceCount = 0;
|
|
522
|
+
let typeCount = 0;
|
|
523
|
+
for (const project of projects) {
|
|
524
|
+
const sourceFiles = project.getSourceFiles();
|
|
525
|
+
fileCount += sourceFiles.length;
|
|
526
|
+
for (const sourceFile of sourceFiles) {
|
|
527
|
+
functionCount += sourceFile.getFunctions().length;
|
|
528
|
+
classCount += sourceFile.getClasses().length;
|
|
529
|
+
interfaceCount += sourceFile.getInterfaces().length;
|
|
530
|
+
typeCount += sourceFile.getTypeAliases().length;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const duration = Date.now() - startTime;
|
|
534
|
+
const memoryUsage = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
535
|
+
return {
|
|
536
|
+
indexed: {
|
|
537
|
+
files: fileCount,
|
|
538
|
+
functions: functionCount,
|
|
539
|
+
classes: classCount,
|
|
540
|
+
interfaces: interfaceCount,
|
|
541
|
+
types: typeCount
|
|
542
|
+
},
|
|
543
|
+
duration_ms: duration,
|
|
544
|
+
memory_mb: Math.round(memoryUsage)
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
var symbolIndex = new SymbolIndex();
|
|
549
|
+
|
|
550
|
+
// src/tools/build-symbol-index.ts
|
|
551
|
+
async function buildSymbolIndex(path2) {
|
|
552
|
+
return await symbolIndex.buildIndex(path2);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// src/index.ts
|
|
556
|
+
var ASTExplorerServer = class {
|
|
557
|
+
server;
|
|
558
|
+
version = "0.1.0";
|
|
559
|
+
constructor() {
|
|
560
|
+
this.server = new import_server.Server(
|
|
561
|
+
{
|
|
562
|
+
name: "ast-explorer-server",
|
|
563
|
+
version: this.version
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
capabilities: {
|
|
567
|
+
tools: {}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
);
|
|
571
|
+
this.setupHandlers();
|
|
572
|
+
this.server.onerror = (error) => {
|
|
573
|
+
console.error("[MCP Error]", error);
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
setupHandlers() {
|
|
577
|
+
this.server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
|
|
578
|
+
return {
|
|
579
|
+
tools: [
|
|
580
|
+
{
|
|
581
|
+
name: "resolve_definition",
|
|
582
|
+
description: "Find where a symbol is defined using TypeScript AST.",
|
|
583
|
+
inputSchema: {
|
|
584
|
+
type: "object",
|
|
585
|
+
properties: {
|
|
586
|
+
symbol: {
|
|
587
|
+
type: "string",
|
|
588
|
+
description: "Symbol name (e.g., function, class)"
|
|
589
|
+
},
|
|
590
|
+
path: {
|
|
591
|
+
type: "string",
|
|
592
|
+
description: "Project root or target directory"
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
required: ["symbol", "path"]
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: "find_references",
|
|
600
|
+
description: "Find all usages of a symbol across the project.",
|
|
601
|
+
inputSchema: {
|
|
602
|
+
type: "object",
|
|
603
|
+
properties: {
|
|
604
|
+
symbol: { type: "string", description: "Symbol name" },
|
|
605
|
+
path: { type: "string", description: "Project root" },
|
|
606
|
+
limit: { type: "number", default: 50 },
|
|
607
|
+
offset: { type: "number", default: 0 }
|
|
608
|
+
},
|
|
609
|
+
required: ["symbol", "path"]
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
name: "find_implementations",
|
|
614
|
+
description: "Find implementations of interfaces or abstract classes.",
|
|
615
|
+
inputSchema: {
|
|
616
|
+
type: "object",
|
|
617
|
+
properties: {
|
|
618
|
+
symbol: { type: "string", description: "Interface/Class name" },
|
|
619
|
+
path: { type: "string", description: "Project root" },
|
|
620
|
+
limit: { type: "number", default: 50 },
|
|
621
|
+
offset: { type: "number", default: 0 }
|
|
622
|
+
},
|
|
623
|
+
required: ["symbol", "path"]
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
name: "get_file_structure",
|
|
628
|
+
description: "Get structural overview of a file (imports, exports, symbols).",
|
|
629
|
+
inputSchema: {
|
|
630
|
+
type: "object",
|
|
631
|
+
properties: {
|
|
632
|
+
file: { type: "string", description: "Absolute path to file" }
|
|
633
|
+
},
|
|
634
|
+
required: ["file"]
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
name: "search_code",
|
|
639
|
+
description: "Fast regex search via bundled ripgrep.",
|
|
640
|
+
inputSchema: {
|
|
641
|
+
type: "object",
|
|
642
|
+
properties: {
|
|
643
|
+
pattern: { type: "string", description: "Search pattern" },
|
|
644
|
+
path: { type: "string", description: "Directory to search" },
|
|
645
|
+
filePattern: { type: "string", description: "Glob filter" },
|
|
646
|
+
limit: { type: "number", default: 50 }
|
|
647
|
+
},
|
|
648
|
+
required: ["pattern", "path"]
|
|
649
|
+
}
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
name: "get_symbol_docs",
|
|
653
|
+
description: "Get JSDoc/TSDoc for a specific symbol.",
|
|
654
|
+
inputSchema: {
|
|
655
|
+
type: "object",
|
|
656
|
+
properties: {
|
|
657
|
+
symbol: { type: "string", description: "Symbol name" },
|
|
658
|
+
path: { type: "string", description: "Project root" }
|
|
659
|
+
},
|
|
660
|
+
required: ["symbol", "path"]
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
name: "build_symbol_index",
|
|
665
|
+
description: "Warm the symbol index for faster navigation.",
|
|
666
|
+
inputSchema: {
|
|
667
|
+
type: "object",
|
|
668
|
+
properties: {
|
|
669
|
+
path: { type: "string", description: "Project root to index" }
|
|
670
|
+
},
|
|
671
|
+
required: ["path"]
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
]
|
|
675
|
+
};
|
|
676
|
+
});
|
|
677
|
+
this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
|
|
678
|
+
const { name, arguments: args } = request.params;
|
|
679
|
+
try {
|
|
680
|
+
switch (name) {
|
|
681
|
+
case "resolve_definition": {
|
|
682
|
+
const { symbol, path: path2 } = ResolveDefinitionSchema.parse(args);
|
|
683
|
+
const results = await resolveDefinition(symbol, path2);
|
|
684
|
+
return {
|
|
685
|
+
content: [
|
|
686
|
+
{ type: "text", text: JSON.stringify(results, null, 2) }
|
|
687
|
+
]
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
case "find_references": {
|
|
691
|
+
const { symbol, path: path2, limit, offset } = FindReferencesSchema.parse(args);
|
|
692
|
+
const results = await findReferences(symbol, path2, limit, offset);
|
|
693
|
+
return {
|
|
694
|
+
content: [
|
|
695
|
+
{ type: "text", text: JSON.stringify(results, null, 2) }
|
|
696
|
+
]
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
case "find_implementations": {
|
|
700
|
+
const { symbol, path: path2, limit, offset } = FindImplementationsSchema.parse(args);
|
|
701
|
+
const results = await findImplementations(
|
|
702
|
+
symbol,
|
|
703
|
+
path2,
|
|
704
|
+
limit,
|
|
705
|
+
offset
|
|
706
|
+
);
|
|
707
|
+
return {
|
|
708
|
+
content: [
|
|
709
|
+
{ type: "text", text: JSON.stringify(results, null, 2) }
|
|
710
|
+
]
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
case "get_file_structure": {
|
|
714
|
+
const { file } = GetFileStructureSchema.parse(args);
|
|
715
|
+
const structure = await getFileStructure(file);
|
|
716
|
+
return {
|
|
717
|
+
content: [
|
|
718
|
+
{ type: "text", text: JSON.stringify(structure, null, 2) }
|
|
719
|
+
]
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
case "search_code": {
|
|
723
|
+
const { pattern, path: path2, filePattern, limit } = SearchCodeSchema.parse(args);
|
|
724
|
+
const results = await searchCode(pattern, path2, filePattern, limit);
|
|
725
|
+
return {
|
|
726
|
+
content: [
|
|
727
|
+
{ type: "text", text: JSON.stringify(results, null, 2) }
|
|
728
|
+
]
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
case "get_symbol_docs": {
|
|
732
|
+
const { symbol, path: path2 } = GetSymbolDocsSchema.parse(args);
|
|
733
|
+
const docs = await getSymbolDocs(symbol, path2);
|
|
734
|
+
return {
|
|
735
|
+
content: [{ type: "text", text: JSON.stringify(docs, null, 2) }]
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
case "build_symbol_index": {
|
|
739
|
+
const { path: path2 } = BuildSymbolIndexSchema.parse(args);
|
|
740
|
+
const stats = await buildSymbolIndex(path2);
|
|
741
|
+
return {
|
|
742
|
+
content: [{ type: "text", text: JSON.stringify(stats, null, 2) }]
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
default:
|
|
746
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
747
|
+
}
|
|
748
|
+
} catch (error) {
|
|
749
|
+
return {
|
|
750
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
751
|
+
isError: true
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
async run() {
|
|
757
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
758
|
+
await this.server.connect(transport);
|
|
759
|
+
console.error("AST Explorer MCP Server started");
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
var server = new ASTExplorerServer();
|
|
763
|
+
server.run().catch((error) => {
|
|
764
|
+
console.error("Fatal error starting AST Explorer MCP Server:", error);
|
|
765
|
+
process.exit(1);
|
|
766
|
+
});
|
|
767
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
768
|
+
0 && (module.exports = {
|
|
769
|
+
ASTExplorerServer
|
|
770
|
+
});
|
|
771
|
+
//# sourceMappingURL=index.cjs.map
|