@ebowwa/dependency-graph-mcp 1.0.0

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/src/index.js ADDED
@@ -0,0 +1,983 @@
1
+ #!/usr/bin/env bun
2
+ "use strict";
3
+ /**
4
+ * Dependency Graph MCP Server
5
+ *
6
+ * Analyzes and visualizes dependency relationships in monorepos.
7
+ * Provides tools for:
8
+ * - Building dependency graphs from package.json and imports
9
+ * - Visualizing dependency relationships
10
+ * - Impact analysis for refactoring decisions
11
+ * - Finding circular dependencies
12
+ * - Identifying unused code
13
+ */
14
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
15
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
16
+ return new (P || (P = Promise))(function (resolve, reject) {
17
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
18
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
19
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
20
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
21
+ });
22
+ };
23
+ var __generator = (this && this.__generator) || function (thisArg, body) {
24
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
25
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
26
+ function verb(n) { return function (v) { return step([n, v]); }; }
27
+ function step(op) {
28
+ if (f) throw new TypeError("Generator is already executing.");
29
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
30
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
31
+ if (y = 0, t) op = [op[0] & 2, t.value];
32
+ switch (op[0]) {
33
+ case 0: case 1: t = op; break;
34
+ case 4: _.label++; return { value: op[1], done: false };
35
+ case 5: _.label++; y = op[1]; op = [0]; continue;
36
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
37
+ default:
38
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
39
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
40
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
41
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
42
+ if (t[2]) _.ops.pop();
43
+ _.trys.pop(); continue;
44
+ }
45
+ op = body.call(thisArg, _);
46
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
47
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
48
+ }
49
+ };
50
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
51
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
52
+ if (ar || !(i in from)) {
53
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
54
+ ar[i] = from[i];
55
+ }
56
+ }
57
+ return to.concat(ar || Array.prototype.slice.call(from));
58
+ };
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ var index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
61
+ var stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
62
+ var types_js_1 = require("@modelcontextprotocol/sdk/types.js");
63
+ var node_fs_1 = require("node:fs");
64
+ var node_path_1 = require("node:path");
65
+ var node_url_1 = require("node:url");
66
+ var TOOLING_DIR = (0, node_path_1.dirname)((0, node_url_1.fileURLToPath)(import.meta.url.replace("/MCP/packages/dependency-graph", "/packages/src/tooling")));
67
+ // ==============
68
+ // Graph Builder
69
+ // ==============
70
+ var DependencyGraphBuilder = /** @class */ (function () {
71
+ function DependencyGraphBuilder(monorepoRoot) {
72
+ this.packageCache = new Map();
73
+ this.monorepoRoot = monorepoRoot;
74
+ this.graph = {
75
+ nodes: new Map(),
76
+ edges: [],
77
+ reverseEdges: new Map(),
78
+ };
79
+ }
80
+ /**
81
+ * Build the complete dependency graph
82
+ */
83
+ DependencyGraphBuilder.prototype.build = function () {
84
+ return __awaiter(this, arguments, void 0, function (options) {
85
+ var _a, includeDevDependencies, _b, analyzeImports, _c, excludePatterns, packages, _i, packages_1, pkg, _d, packages_2, pkg;
86
+ if (options === void 0) { options = {}; }
87
+ return __generator(this, function (_e) {
88
+ switch (_e.label) {
89
+ case 0:
90
+ _a = options.includeDevDependencies, includeDevDependencies = _a === void 0 ? false : _a, _b = options.analyzeImports, analyzeImports = _b === void 0 ? true : _b, _c = options.excludePatterns, excludePatterns = _c === void 0 ? [] : _c;
91
+ return [4 /*yield*/, this.discoverPackages()];
92
+ case 1:
93
+ packages = _e.sent();
94
+ // Add nodes for all packages
95
+ for (_i = 0, packages_1 = packages; _i < packages_1.length; _i++) {
96
+ pkg = packages_1[_i];
97
+ this.addNode(pkg.name, pkg.path, "workspace", pkg.version);
98
+ }
99
+ _d = 0, packages_2 = packages;
100
+ _e.label = 2;
101
+ case 2:
102
+ if (!(_d < packages_2.length)) return [3 /*break*/, 6];
103
+ pkg = packages_2[_d];
104
+ return [4 /*yield*/, this.analyzePackageDependencies(pkg, includeDevDependencies, excludePatterns)];
105
+ case 3:
106
+ _e.sent();
107
+ if (!analyzeImports) return [3 /*break*/, 5];
108
+ return [4 /*yield*/, this.analyzeImports(pkg)];
109
+ case 4:
110
+ _e.sent();
111
+ _e.label = 5;
112
+ case 5:
113
+ _d++;
114
+ return [3 /*break*/, 2];
115
+ case 6:
116
+ // Build reverse edges for impact analysis
117
+ this.buildReverseEdges();
118
+ return [2 /*return*/, this.graph];
119
+ }
120
+ });
121
+ });
122
+ };
123
+ /**
124
+ * Discover all package.json files in the monorepo
125
+ */
126
+ DependencyGraphBuilder.prototype.discoverPackages = function () {
127
+ return __awaiter(this, void 0, void 0, function () {
128
+ var packages, seen, searchPaths, _i, searchPaths_1, searchPath;
129
+ return __generator(this, function (_a) {
130
+ switch (_a.label) {
131
+ case 0:
132
+ packages = [];
133
+ seen = new Set();
134
+ searchPaths = [
135
+ this.monorepoRoot,
136
+ (0, node_path_1.join)(this.monorepoRoot, "packages"),
137
+ (0, node_path_1.join)(this.monorepoRoot, "packages/src"),
138
+ (0, node_path_1.join)(this.monorepoRoot, "apps"),
139
+ (0, node_path_1.join)(this.monorepoRoot, "MCP/packages"),
140
+ ];
141
+ _i = 0, searchPaths_1 = searchPaths;
142
+ _a.label = 1;
143
+ case 1:
144
+ if (!(_i < searchPaths_1.length)) return [3 /*break*/, 4];
145
+ searchPath = searchPaths_1[_i];
146
+ if (!(0, node_fs_1.existsSync)(searchPath))
147
+ return [3 /*break*/, 3];
148
+ return [4 /*yield*/, this.searchDirectory(searchPath, packages, seen, 0, 4)];
149
+ case 2:
150
+ _a.sent();
151
+ _a.label = 3;
152
+ case 3:
153
+ _i++;
154
+ return [3 /*break*/, 1];
155
+ case 4: return [2 /*return*/, packages];
156
+ }
157
+ });
158
+ });
159
+ };
160
+ /**
161
+ * Recursively search for package.json files
162
+ */
163
+ DependencyGraphBuilder.prototype.searchDirectory = function (dir, packages, seen, depth, maxDepth) {
164
+ return __awaiter(this, void 0, void 0, function () {
165
+ var entries, _i, entries_1, entry, fullPath, packageDir, relativePath, pkg, _a;
166
+ return __generator(this, function (_b) {
167
+ switch (_b.label) {
168
+ case 0:
169
+ if (depth > maxDepth)
170
+ return [2 /*return*/];
171
+ _b.label = 1;
172
+ case 1:
173
+ _b.trys.push([1, 7, , 8]);
174
+ entries = (0, node_fs_1.readdirSync)(dir, { withFileTypes: true });
175
+ _i = 0, entries_1 = entries;
176
+ _b.label = 2;
177
+ case 2:
178
+ if (!(_i < entries_1.length)) return [3 /*break*/, 6];
179
+ entry = entries_1[_i];
180
+ // Skip node_modules and hidden dirs
181
+ if (entry.name === "node_modules" || entry.name.startsWith("."))
182
+ return [3 /*break*/, 5];
183
+ if (entry.name === "dist" || entry.name === "build")
184
+ return [3 /*break*/, 5];
185
+ fullPath = (0, node_path_1.join)(dir, entry.name);
186
+ if (!entry.isDirectory()) return [3 /*break*/, 4];
187
+ return [4 /*yield*/, this.searchDirectory(fullPath, packages, seen, depth + 1, maxDepth)];
188
+ case 3:
189
+ _b.sent();
190
+ return [3 /*break*/, 5];
191
+ case 4:
192
+ if (entry.name === "package.json") {
193
+ packageDir = (0, node_path_1.dirname)(fullPath);
194
+ relativePath = (0, node_path_1.relative)(this.monorepoRoot, packageDir);
195
+ if (seen.has(relativePath))
196
+ return [3 /*break*/, 5];
197
+ seen.add(relativePath);
198
+ try {
199
+ pkg = JSON.parse((0, node_fs_1.readFileSync)(fullPath, "utf-8"));
200
+ if (pkg.name && !pkg.private) {
201
+ packages.push({
202
+ name: pkg.name,
203
+ path: relativePath,
204
+ version: pkg.version || "0.0.0",
205
+ });
206
+ this.packageCache.set(pkg.name, { path: relativePath, pkg: pkg });
207
+ }
208
+ }
209
+ catch (_c) {
210
+ // Invalid package.json, skip
211
+ }
212
+ }
213
+ _b.label = 5;
214
+ case 5:
215
+ _i++;
216
+ return [3 /*break*/, 2];
217
+ case 6: return [3 /*break*/, 8];
218
+ case 7:
219
+ _a = _b.sent();
220
+ return [3 /*break*/, 8];
221
+ case 8: return [2 /*return*/];
222
+ }
223
+ });
224
+ });
225
+ };
226
+ /**
227
+ * Analyze package.json dependencies
228
+ */
229
+ DependencyGraphBuilder.prototype.analyzePackageDependencies = function (pkg, includeDev, excludePatterns) {
230
+ return __awaiter(this, void 0, void 0, function () {
231
+ var pkgPath, packageJson, depTypes, _i, depTypes_1, depType, deps, _loop_1, this_1, _a, _b, _c, depName, depVersion;
232
+ return __generator(this, function (_d) {
233
+ pkgPath = (0, node_path_1.join)(this.monorepoRoot, pkg.path, "package.json");
234
+ if (!(0, node_fs_1.existsSync)(pkgPath))
235
+ return [2 /*return*/];
236
+ packageJson = JSON.parse((0, node_fs_1.readFileSync)(pkgPath, "utf-8"));
237
+ depTypes = ["dependencies"];
238
+ if (includeDev)
239
+ depTypes.push("devDependencies", "peerDependencies", "optionalDependencies");
240
+ for (_i = 0, depTypes_1 = depTypes; _i < depTypes_1.length; _i++) {
241
+ depType = depTypes_1[_i];
242
+ deps = packageJson[depType];
243
+ if (!deps)
244
+ continue;
245
+ _loop_1 = function (depName, depVersion) {
246
+ // Check if this matches any exclude pattern
247
+ if (excludePatterns.some(function (pattern) { return depName.match(pattern); }))
248
+ return "continue";
249
+ var isWorkspace = depVersion === "workspace:*" || depVersion === "workspace:^" || depVersion === "workspace:~";
250
+ if (isWorkspace) {
251
+ // This is a workspace dependency
252
+ // Find the actual package in our cache
253
+ var targetPkg = this_1.packageCache.get(depName);
254
+ if (targetPkg) {
255
+ this_1.addEdge(pkg.name, depName, "workspace");
256
+ }
257
+ }
258
+ else if (this_1.packageCache.has(depName)) {
259
+ // It's a local package but not using workspace: protocol
260
+ this_1.addEdge(pkg.name, depName, "workspace");
261
+ }
262
+ else {
263
+ // External dependency
264
+ this_1.addNode(depName, "", "external", depVersion);
265
+ this_1.addEdge(pkg.name, depName, "external");
266
+ }
267
+ };
268
+ this_1 = this;
269
+ for (_a = 0, _b = Object.entries(deps); _a < _b.length; _a++) {
270
+ _c = _b[_a], depName = _c[0], depVersion = _c[1];
271
+ _loop_1(depName, depVersion);
272
+ }
273
+ }
274
+ return [2 /*return*/];
275
+ });
276
+ });
277
+ };
278
+ /**
279
+ * Analyze TypeScript/JavaScript imports
280
+ */
281
+ DependencyGraphBuilder.prototype.analyzeImports = function (pkg) {
282
+ return __awaiter(this, void 0, void 0, function () {
283
+ var pkgDir, sourceDirs, _i, sourceDirs_1, sourceDir, searchPath;
284
+ return __generator(this, function (_a) {
285
+ switch (_a.label) {
286
+ case 0:
287
+ pkgDir = (0, node_path_1.join)(this.monorepoRoot, pkg.path);
288
+ sourceDirs = ["src", "lib", ""];
289
+ _i = 0, sourceDirs_1 = sourceDirs;
290
+ _a.label = 1;
291
+ case 1:
292
+ if (!(_i < sourceDirs_1.length)) return [3 /*break*/, 4];
293
+ sourceDir = sourceDirs_1[_i];
294
+ searchPath = sourceDir ? (0, node_path_1.join)(pkgDir, sourceDir) : pkgDir;
295
+ if (!(0, node_fs_1.existsSync)(searchPath))
296
+ return [3 /*break*/, 3];
297
+ return [4 /*yield*/, this.analyzeImportsInDirectory(pkg.name, searchPath)];
298
+ case 2:
299
+ _a.sent();
300
+ _a.label = 3;
301
+ case 3:
302
+ _i++;
303
+ return [3 /*break*/, 1];
304
+ case 4: return [2 /*return*/];
305
+ }
306
+ });
307
+ });
308
+ };
309
+ /**
310
+ * Recursively analyze imports in a directory
311
+ */
312
+ DependencyGraphBuilder.prototype.analyzeImportsInDirectory = function (packageName, dir) {
313
+ return __awaiter(this, void 0, void 0, function () {
314
+ var entries, _i, entries_2, entry, fullPath, _a;
315
+ return __generator(this, function (_b) {
316
+ switch (_b.label) {
317
+ case 0:
318
+ _b.trys.push([0, 7, , 8]);
319
+ entries = (0, node_fs_1.readdirSync)(dir, { withFileTypes: true });
320
+ _i = 0, entries_2 = entries;
321
+ _b.label = 1;
322
+ case 1:
323
+ if (!(_i < entries_2.length)) return [3 /*break*/, 6];
324
+ entry = entries_2[_i];
325
+ if (entry.name === "node_modules" || entry.name.startsWith("."))
326
+ return [3 /*break*/, 5];
327
+ if (entry.name === "dist" || entry.name === "build")
328
+ return [3 /*break*/, 5];
329
+ fullPath = (0, node_path_1.join)(dir, entry.name);
330
+ if (!entry.isDirectory()) return [3 /*break*/, 3];
331
+ return [4 /*yield*/, this.analyzeImportsInDirectory(packageName, fullPath)];
332
+ case 2:
333
+ _b.sent();
334
+ return [3 /*break*/, 5];
335
+ case 3:
336
+ if (!(entry.name.endsWith(".ts") || entry.name.endsWith(".tsx") || entry.name.endsWith(".js") || entry.name.endsWith(".jsx") || entry.name.endsWith(".mjs"))) return [3 /*break*/, 5];
337
+ return [4 /*yield*/, this.analyzeImportsInFile(packageName, fullPath)];
338
+ case 4:
339
+ _b.sent();
340
+ _b.label = 5;
341
+ case 5:
342
+ _i++;
343
+ return [3 /*break*/, 1];
344
+ case 6: return [3 /*break*/, 8];
345
+ case 7:
346
+ _a = _b.sent();
347
+ return [3 /*break*/, 8];
348
+ case 8: return [2 /*return*/];
349
+ }
350
+ });
351
+ });
352
+ };
353
+ /**
354
+ * Analyze imports in a single file
355
+ */
356
+ DependencyGraphBuilder.prototype.analyzeImportsInFile = function (packageName, filePath) {
357
+ return __awaiter(this, void 0, void 0, function () {
358
+ var content, importPatterns, _i, importPatterns_1, pattern, match, importPath, _a, _b, _c, pkgName, pkgData;
359
+ return __generator(this, function (_d) {
360
+ try {
361
+ content = (0, node_fs_1.readFileSync)(filePath, "utf-8");
362
+ importPatterns = [
363
+ // ES imports: import ... from '...' | import ... from "..."
364
+ /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s*,?\s*)*\s+from\s+['"]([^'"]+)['"]/g,
365
+ // Dynamic imports: import('...')
366
+ /import\(['"]([^'"]+)['"]\)/g,
367
+ // require(): require('...') | require("...")
368
+ /require\(['"]([^'"]+)['"]\)/g,
369
+ // Export from: export ... from '...'
370
+ /export\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+)\s+from\s+)?['"]([^'"]+)['"]/g,
371
+ ];
372
+ for (_i = 0, importPatterns_1 = importPatterns; _i < importPatterns_1.length; _i++) {
373
+ pattern = importPatterns_1[_i];
374
+ match = void 0;
375
+ while ((match = pattern.exec(content)) !== null) {
376
+ importPath = match[1];
377
+ // Skip relative imports
378
+ if (importPath.startsWith(".") || importPath.startsWith("/"))
379
+ continue;
380
+ // Check if this is a known workspace package
381
+ for (_a = 0, _b = this.packageCache; _a < _b.length; _a++) {
382
+ _c = _b[_a], pkgName = _c[0], pkgData = _c[1];
383
+ if (importPath === pkgName || importPath.startsWith(pkgName + "/")) {
384
+ this.addEdge(packageName, pkgName, "import", importPath);
385
+ break;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+ catch (_e) {
392
+ // File not readable
393
+ }
394
+ return [2 /*return*/];
395
+ });
396
+ });
397
+ };
398
+ /**
399
+ * Add a node to the graph
400
+ */
401
+ DependencyGraphBuilder.prototype.addNode = function (name, path, type, version) {
402
+ if (!this.graph.nodes.has(name)) {
403
+ this.graph.nodes.set(name, { name: name, path: path, type: type, version: version });
404
+ this.graph.reverseEdges.set(name, new Set());
405
+ }
406
+ };
407
+ /**
408
+ * Add an edge to the graph
409
+ */
410
+ DependencyGraphBuilder.prototype.addEdge = function (from, to, type, importPath) {
411
+ // Skip self-references
412
+ if (from === to)
413
+ return;
414
+ // Check if edge already exists
415
+ var existing = this.graph.edges.find(function (e) { return e.from === from && e.to === to; });
416
+ if (existing)
417
+ return;
418
+ this.graph.edges.push({ from: from, to: to, type: type, importPath: importPath });
419
+ // Update reverse edges
420
+ if (!this.graph.reverseEdges.has(to)) {
421
+ this.graph.reverseEdges.set(to, new Set());
422
+ }
423
+ this.graph.reverseEdges.get(to).add(from);
424
+ };
425
+ /**
426
+ * Build reverse edges for impact analysis
427
+ */
428
+ DependencyGraphBuilder.prototype.buildReverseEdges = function () {
429
+ for (var _i = 0, _a = this.graph.reverseEdges; _i < _a.length; _i++) {
430
+ var _b = _a[_i], to = _b[0], dependents = _b[1];
431
+ // Ensure node exists
432
+ if (!this.graph.nodes.has(to)) {
433
+ this.graph.nodes.set(to, { name: to, path: "", type: "external" });
434
+ }
435
+ }
436
+ };
437
+ /**
438
+ * Get the built graph
439
+ */
440
+ DependencyGraphBuilder.prototype.getGraph = function () {
441
+ return this.graph;
442
+ };
443
+ return DependencyGraphBuilder;
444
+ }());
445
+ // ==============
446
+ // MCP Server
447
+ // ==============
448
+ var DEPENDENCY_GRAPH_SCHEMA = {
449
+ name: "dependency_graph",
450
+ description: "Build a complete dependency graph of the monorepo",
451
+ inputSchema: {
452
+ type: "object",
453
+ properties: {
454
+ includeDevDependencies: {
455
+ type: "boolean",
456
+ description: "Include devDependencies in the graph",
457
+ default: false,
458
+ },
459
+ analyzeImports: {
460
+ type: "boolean",
461
+ description: "Analyze TypeScript/JavaScript imports",
462
+ default: true,
463
+ },
464
+ excludePatterns: {
465
+ type: "array",
466
+ items: { type: "string" },
467
+ description: "Regex patterns to exclude from dependency analysis",
468
+ default: [],
469
+ },
470
+ format: {
471
+ type: "string",
472
+ enum: ["json", "mermaid", "dot", "tree"],
473
+ description: "Output format for the graph",
474
+ default: "json",
475
+ },
476
+ },
477
+ },
478
+ };
479
+ var IMPACT_ANALYSIS_SCHEMA = {
480
+ name: "impact_analysis",
481
+ description: "Analyze the impact of changing a specific package",
482
+ inputSchema: {
483
+ type: "object",
484
+ properties: {
485
+ package: {
486
+ type: "string",
487
+ description: "Package name to analyze",
488
+ },
489
+ includeTransitive: {
490
+ type: "boolean",
491
+ description: "Include transitive dependents",
492
+ default: true,
493
+ },
494
+ format: {
495
+ type: "string",
496
+ enum: ["json", "tree"],
497
+ description: "Output format",
498
+ default: "tree",
499
+ },
500
+ },
501
+ required: ["package"],
502
+ },
503
+ };
504
+ var FIND_CIRCULAR_SCHEMA = {
505
+ name: "find_circular",
506
+ description: "Find circular dependencies in the monorepo",
507
+ inputSchema: {
508
+ type: "object",
509
+ properties: {
510
+ maxDepth: {
511
+ type: "number",
512
+ description: "Maximum depth to search for cycles",
513
+ default: 10,
514
+ },
515
+ },
516
+ },
517
+ };
518
+ var UNUSED_CODE_SCHEMA = {
519
+ name: "unused_code",
520
+ description: "Find potentially unused packages (no dependents)",
521
+ inputSchema: {
522
+ type: "object",
523
+ properties: {
524
+ includeExternal: {
525
+ type: "boolean",
526
+ description: "Include external dependencies",
527
+ default: false,
528
+ },
529
+ },
530
+ },
531
+ };
532
+ var PACKAGE_INFO_SCHEMA = {
533
+ name: "package_info",
534
+ description: "Get detailed information about a specific package",
535
+ inputSchema: {
536
+ type: "object",
537
+ properties: {
538
+ package: {
539
+ type: "string",
540
+ description: "Package name",
541
+ },
542
+ },
543
+ required: ["package"],
544
+ },
545
+ };
546
+ // Format the graph for output
547
+ function formatGraph(graph, format) {
548
+ switch (format) {
549
+ case "mermaid":
550
+ return formatAsMermaid(graph);
551
+ case "dot":
552
+ return formatAsDot(graph);
553
+ case "tree":
554
+ return formatAsTree(graph);
555
+ case "json":
556
+ default:
557
+ return JSON.stringify({
558
+ nodes: Array.from(graph.nodes.values()),
559
+ edges: graph.edges,
560
+ }, null, 2);
561
+ }
562
+ }
563
+ function formatAsMermaid(graph) {
564
+ var lines = ["graph TD"];
565
+ // Add nodes
566
+ for (var _i = 0, _a = graph.nodes; _i < _a.length; _i++) {
567
+ var _b = _a[_i], name_1 = _b[0], node = _b[1];
568
+ var label = node.type === "workspace" ? "\uD83D\uDCE6 ".concat(name_1) : "\uD83D\uDCDA ".concat(name_1);
569
+ lines.push(" ".concat(name_1.replace(/[^a-zA-Z0-9]/g, "_"), "[\"").concat(label, "\"]"));
570
+ }
571
+ // Add edges
572
+ for (var _c = 0, _d = graph.edges; _c < _d.length; _c++) {
573
+ var edge = _d[_c];
574
+ var from = edge.from.replace(/[^a-zA-Z0-9]/g, "_");
575
+ var to = edge.to.replace(/[^a-zA-Z0-9]/g, "_");
576
+ var label = edge.type === "workspace" ? "workspace" : edge.type === "import" ? "imports" : "external";
577
+ lines.push(" ".concat(from, " -->|").concat(label, "| ").concat(to));
578
+ }
579
+ // Add styles
580
+ lines.push(' classDef workspace fill:#e1f5fe');
581
+ lines.push(' classDef external fill:#f5f5f5');
582
+ lines.push(' classDef import fill:#fff3e0');
583
+ for (var _e = 0, _f = graph.nodes; _e < _f.length; _e++) {
584
+ var _g = _f[_e], name_2 = _g[0], node = _g[1];
585
+ var id = name_2.replace(/[^a-zA-Z0-9]/g, "_");
586
+ if (node.type === "workspace") {
587
+ lines.push(" class ".concat(id, " workspace"));
588
+ }
589
+ else if (node.type === "external") {
590
+ lines.push(" class ".concat(id, " external"));
591
+ }
592
+ }
593
+ return lines.join("\n");
594
+ }
595
+ function formatAsDot(graph) {
596
+ var lines = ["digraph dependencies {"];
597
+ lines.push(' rankdir=LR;');
598
+ lines.push(' node [shape=box];');
599
+ // Add nodes
600
+ for (var _i = 0, _a = graph.nodes; _i < _a.length; _i++) {
601
+ var _b = _a[_i], name_3 = _b[0], node = _b[1];
602
+ var color = node.type === "workspace" ? "lightblue" : "lightgray";
603
+ lines.push(" \"".concat(name_3, "\" [fillcolor=").concat(color, ", style=filled];"));
604
+ }
605
+ // Add edges
606
+ for (var _c = 0, _d = graph.edges; _c < _d.length; _c++) {
607
+ var edge = _d[_c];
608
+ var style = edge.type === "workspace" ? "solid" : "dashed";
609
+ lines.push(" \"".concat(edge.from, "\" -> \"").concat(edge.to, "\" [style=").concat(style, ", label=\"").concat(edge.type, "\"];"));
610
+ }
611
+ lines.push("}");
612
+ return lines.join("\n");
613
+ }
614
+ function formatAsTree(graph) {
615
+ var _a;
616
+ var lines = [];
617
+ var seen = new Set();
618
+ // Find root nodes (no incoming edges from other workspace packages)
619
+ var workspaceNodes = Array.from(graph.nodes.values()).filter(function (n) { return n.type === "workspace"; });
620
+ var incomingEdges = new Map();
621
+ for (var _i = 0, workspaceNodes_1 = workspaceNodes; _i < workspaceNodes_1.length; _i++) {
622
+ var node = workspaceNodes_1[_i];
623
+ incomingEdges.set(node.name, 0);
624
+ }
625
+ for (var _b = 0, _c = graph.edges; _b < _c.length; _b++) {
626
+ var edge = _c[_b];
627
+ if (((_a = graph.nodes.get(edge.to)) === null || _a === void 0 ? void 0 : _a.type) === "workspace") {
628
+ incomingEdges.set(edge.to, (incomingEdges.get(edge.to) || 0) + 1);
629
+ }
630
+ }
631
+ // Start from roots
632
+ for (var _d = 0, _e = graph.nodes; _d < _e.length; _d++) {
633
+ var _f = _e[_d], name_4 = _f[0], node = _f[1];
634
+ if (node.type === "workspace" && (incomingEdges.get(name_4) || 0) === 0) {
635
+ lines.push("\uD83D\uDCE6 ".concat(name_4));
636
+ printTree(graph, name_4, "", seen, lines);
637
+ }
638
+ }
639
+ // Add any disconnected nodes
640
+ for (var _g = 0, _h = graph.nodes; _g < _h.length; _g++) {
641
+ var _j = _h[_g], name_5 = _j[0], node = _j[1];
642
+ if (!seen.has(name_5) && node.type === "workspace") {
643
+ lines.push("\uD83D\uDCE6 ".concat(name_5, " (disconnected)"));
644
+ }
645
+ }
646
+ return lines.join("\n");
647
+ }
648
+ function printTree(graph, nodeName, prefix, seen, lines) {
649
+ seen.add(nodeName);
650
+ // Find outgoing edges to other workspace packages
651
+ var outgoing = graph.edges.filter(function (e) { var _a; return e.from === nodeName && ((_a = graph.nodes.get(e.to)) === null || _a === void 0 ? void 0 : _a.type) === "workspace"; });
652
+ for (var i = 0; i < outgoing.length; i++) {
653
+ var edge = outgoing[i];
654
+ var isLast = i === outgoing.length - 1;
655
+ var connector = isLast ? "└──" : "├──";
656
+ var childPrefix = prefix + (isLast ? " " : "│ ");
657
+ lines.push("".concat(prefix).concat(connector, " \uD83D\uDCE6 ").concat(edge.to, " [").concat(edge.type, "]"));
658
+ if (!seen.has(edge.to)) {
659
+ printTree(graph, edge.to, childPrefix, seen, lines);
660
+ }
661
+ }
662
+ // Show external dependencies count
663
+ var externals = graph.edges.filter(function (e) { var _a; return e.from === nodeName && ((_a = graph.nodes.get(e.to)) === null || _a === void 0 ? void 0 : _a.type) === "external"; });
664
+ if (externals.length > 0) {
665
+ lines.push("".concat(prefix, "\u2514\u2500\u2500 \uD83D\uDCDA ").concat(externals.length, " external dependencies"));
666
+ }
667
+ }
668
+ // Find circular dependencies using DFS
669
+ function findCircularDependencies(graph, maxDepth) {
670
+ if (maxDepth === void 0) { maxDepth = 10; }
671
+ var cycles = [];
672
+ var visited = new Set();
673
+ var recursionStack = new Set();
674
+ function dfs(node, path) {
675
+ if (path.length > maxDepth)
676
+ return;
677
+ visited.add(node);
678
+ recursionStack.add(node);
679
+ path.push(node);
680
+ // Check all outgoing edges
681
+ for (var _i = 0, _a = graph.edges; _i < _a.length; _i++) {
682
+ var edge = _a[_i];
683
+ if (edge.from === node) {
684
+ if (recursionStack.has(edge.to)) {
685
+ // Found a cycle
686
+ var cycleStart = path.indexOf(edge.to);
687
+ if (cycleStart >= 0) {
688
+ cycles.push(__spreadArray(__spreadArray([], path.slice(cycleStart), true), [edge.to], false));
689
+ }
690
+ }
691
+ else if (!visited.has(edge.to)) {
692
+ dfs(edge.to, __spreadArray([], path, true));
693
+ }
694
+ }
695
+ }
696
+ recursionStack.delete(node);
697
+ }
698
+ // Start DFS from each workspace node
699
+ for (var _i = 0, _a = graph.nodes; _i < _a.length; _i++) {
700
+ var _b = _a[_i], name_6 = _b[0], node = _b[1];
701
+ if (node.type === "workspace" && !visited.has(name_6)) {
702
+ dfs(name_6, []);
703
+ }
704
+ }
705
+ return cycles;
706
+ }
707
+ // Impact analysis
708
+ function analyzeImpact(graph, packageName, includeTransitive) {
709
+ if (includeTransitive === void 0) { includeTransitive = true; }
710
+ var direct = [];
711
+ var transitive = [];
712
+ var visited = new Set();
713
+ // Get direct dependents
714
+ var directDependents = graph.reverseEdges.get(packageName);
715
+ if (directDependents) {
716
+ for (var _i = 0, directDependents_1 = directDependents; _i < directDependents_1.length; _i++) {
717
+ var dep = directDependents_1[_i];
718
+ direct.push(dep);
719
+ visited.add(dep);
720
+ }
721
+ }
722
+ // Get transitive dependents
723
+ if (includeTransitive) {
724
+ for (var _a = 0, direct_1 = direct; _a < direct_1.length; _a++) {
725
+ var dep = direct_1[_a];
726
+ collectTransitive(graph, dep, visited, transitive);
727
+ }
728
+ }
729
+ return {
730
+ direct: direct,
731
+ transitive: transitive,
732
+ all: Array.from(visited),
733
+ };
734
+ }
735
+ function collectTransitive(graph, packageName, visited, result) {
736
+ var dependents = graph.reverseEdges.get(packageName);
737
+ if (!dependents)
738
+ return;
739
+ for (var _i = 0, dependents_1 = dependents; _i < dependents_1.length; _i++) {
740
+ var dep = dependents_1[_i];
741
+ if (!visited.has(dep)) {
742
+ visited.add(dep);
743
+ result.push(dep);
744
+ collectTransitive(graph, dep, visited, result);
745
+ }
746
+ }
747
+ }
748
+ // Start the MCP server
749
+ var server = new index_js_1.Server({
750
+ name: "@mcp/dependency-graph",
751
+ version: "0.1.0",
752
+ }, {
753
+ capabilities: {
754
+ tools: {},
755
+ },
756
+ });
757
+ var cachedGraph = null;
758
+ var cachedBuilder = null;
759
+ // List tools
760
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, function () { return __awaiter(void 0, void 0, void 0, function () {
761
+ return __generator(this, function (_a) {
762
+ return [2 /*return*/, ({
763
+ tools: [
764
+ DEPENDENCY_GRAPH_SCHEMA,
765
+ IMPACT_ANALYSIS_SCHEMA,
766
+ FIND_CIRCULAR_SCHEMA,
767
+ UNUSED_CODE_SCHEMA,
768
+ PACKAGE_INFO_SCHEMA,
769
+ ],
770
+ })];
771
+ });
772
+ }); });
773
+ // Handle tool calls
774
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, function (request) { return __awaiter(void 0, void 0, void 0, function () {
775
+ var _a, name, args, monorepoRoot, _b, _c, _d, includeDevDependencies, _e, analyzeImports, _f, excludePatterns, _g, format, graph, _h, packageName, _j, includeTransitive, _k, format, impact, lines, _i, _l, dep, _m, _o, dep, _p, maxDepth, cycles, lines, i, _q, includeExternal, unused, _r, _s, _t, name_7, node, dependents, packageName_1, node, dependencies, dependents, lines, _u, dependencies_1, dep, depNode, _v, dependents_2, dep, error_1;
776
+ return __generator(this, function (_w) {
777
+ switch (_w.label) {
778
+ case 0:
779
+ _a = request.params, name = _a.name, args = _a.arguments;
780
+ // Initialize graph builder if needed
781
+ if (!cachedBuilder) {
782
+ monorepoRoot = process.cwd();
783
+ cachedBuilder = new DependencyGraphBuilder(monorepoRoot);
784
+ }
785
+ if (!!cachedGraph) return [3 /*break*/, 2];
786
+ return [4 /*yield*/, cachedBuilder.build()];
787
+ case 1:
788
+ _w.sent();
789
+ cachedGraph = cachedBuilder.getGraph();
790
+ _w.label = 2;
791
+ case 2:
792
+ _w.trys.push([2, 11, , 12]);
793
+ _b = name;
794
+ switch (_b) {
795
+ case "dependency_graph": return [3 /*break*/, 3];
796
+ case "impact_analysis": return [3 /*break*/, 5];
797
+ case "find_circular": return [3 /*break*/, 6];
798
+ case "unused_code": return [3 /*break*/, 7];
799
+ case "package_info": return [3 /*break*/, 8];
800
+ }
801
+ return [3 /*break*/, 9];
802
+ case 3:
803
+ _c = args, _d = _c.includeDevDependencies, includeDevDependencies = _d === void 0 ? false : _d, _e = _c.analyzeImports, analyzeImports = _e === void 0 ? true : _e, _f = _c.excludePatterns, excludePatterns = _f === void 0 ? [] : _f, _g = _c.format, format = _g === void 0 ? "json" : _g;
804
+ return [4 /*yield*/, cachedBuilder.build({ includeDevDependencies: includeDevDependencies, analyzeImports: analyzeImports, excludePatterns: excludePatterns })];
805
+ case 4:
806
+ _w.sent();
807
+ graph = cachedBuilder.getGraph();
808
+ cachedGraph = graph;
809
+ return [2 /*return*/, {
810
+ content: [{ type: "text", text: formatGraph(graph, format) }],
811
+ }];
812
+ case 5:
813
+ {
814
+ _h = args, packageName = _h.package, _j = _h.includeTransitive, includeTransitive = _j === void 0 ? true : _j, _k = _h.format, format = _k === void 0 ? "tree" : _k;
815
+ impact = analyzeImpact(cachedGraph, packageName, includeTransitive);
816
+ if (format === "json") {
817
+ return [2 /*return*/, {
818
+ content: [{ type: "text", text: JSON.stringify(impact, null, 2) }],
819
+ }];
820
+ }
821
+ else {
822
+ lines = ["Impact analysis for: ".concat(packageName)];
823
+ lines.push("\nDirect dependents (".concat(impact.direct.length, "):"));
824
+ for (_i = 0, _l = impact.direct; _i < _l.length; _i++) {
825
+ dep = _l[_i];
826
+ lines.push(" \u2514\u2500\u2500 ".concat(dep));
827
+ }
828
+ if (includeTransitive && impact.transitive.length > 0) {
829
+ lines.push("\nTransitive dependents (".concat(impact.transitive.length, "):"));
830
+ for (_m = 0, _o = impact.transitive; _m < _o.length; _m++) {
831
+ dep = _o[_m];
832
+ lines.push(" \u2514\u2500\u2500 ".concat(dep));
833
+ }
834
+ }
835
+ lines.push("\nTotal affected: ".concat(impact.all.length, " packages"));
836
+ return [2 /*return*/, { content: [{ type: "text", text: lines.join("\n") }] }];
837
+ }
838
+ }
839
+ _w.label = 6;
840
+ case 6:
841
+ {
842
+ _p = args.maxDepth, maxDepth = _p === void 0 ? 10 : _p;
843
+ cycles = findCircularDependencies(cachedGraph, maxDepth);
844
+ if (cycles.length === 0) {
845
+ return [2 /*return*/, {
846
+ content: [{ type: "text", text: "No circular dependencies found!" }],
847
+ }];
848
+ }
849
+ lines = ["Found ".concat(cycles.length, " circular dependencies:\n")];
850
+ for (i = 0; i < cycles.length; i++) {
851
+ lines.push("Cycle ".concat(i + 1, ":"));
852
+ lines.push(" ".concat(cycles[i].join(" → ")));
853
+ lines.push("");
854
+ }
855
+ return [2 /*return*/, { content: [{ type: "text", text: lines.join("\n") }] }];
856
+ }
857
+ _w.label = 7;
858
+ case 7:
859
+ {
860
+ _q = args.includeExternal, includeExternal = _q === void 0 ? false : _q;
861
+ unused = [];
862
+ for (_r = 0, _s = cachedGraph.nodes; _r < _s.length; _r++) {
863
+ _t = _s[_r], name_7 = _t[0], node = _t[1];
864
+ if (node.type === "workspace" || includeExternal) {
865
+ dependents = cachedGraph.reverseEdges.get(name_7);
866
+ if (!dependents || dependents.size === 0) {
867
+ unused.push(name_7);
868
+ }
869
+ }
870
+ }
871
+ if (unused.length === 0) {
872
+ return [2 /*return*/, {
873
+ content: [{ type: "text", text: "No unused packages found!" }],
874
+ }];
875
+ }
876
+ return [2 /*return*/, {
877
+ content: [
878
+ {
879
+ type: "text",
880
+ text: "Found ".concat(unused.length, " potentially unused packages:\n\n").concat(unused.map(function (n) { return " \u2514\u2500\u2500 ".concat(n); }).join("\n")),
881
+ },
882
+ ],
883
+ }];
884
+ }
885
+ _w.label = 8;
886
+ case 8:
887
+ {
888
+ packageName_1 = args.package;
889
+ node = cachedGraph.nodes.get(packageName_1);
890
+ if (!node) {
891
+ return [2 /*return*/, {
892
+ content: [{ type: "text", text: "Package \"".concat(packageName_1, "\" not found in dependency graph.") }],
893
+ }];
894
+ }
895
+ dependencies = cachedGraph.edges.filter(function (e) { return e.from === packageName_1; });
896
+ dependents = Array.from(cachedGraph.reverseEdges.get(packageName_1) || []);
897
+ lines = [
898
+ "Package: ".concat(packageName_1),
899
+ "Type: ".concat(node.type),
900
+ "Path: ".concat(node.path || "N/A"),
901
+ node.version ? "Version: ".concat(node.version) : "",
902
+ "",
903
+ "Dependencies (".concat(dependencies.length, "):"),
904
+ ];
905
+ for (_u = 0, dependencies_1 = dependencies; _u < dependencies_1.length; _u++) {
906
+ dep = dependencies_1[_u];
907
+ depNode = cachedGraph.nodes.get(dep.to);
908
+ lines.push(" \u2514\u2500\u2500 ".concat(dep.to, " [").concat(dep.type, "]").concat((depNode === null || depNode === void 0 ? void 0 : depNode.version) ? " @ ".concat(depNode.version) : ""));
909
+ }
910
+ lines.push("", "Dependents (".concat(dependents.length, "):"));
911
+ for (_v = 0, dependents_2 = dependents; _v < dependents_2.length; _v++) {
912
+ dep = dependents_2[_v];
913
+ lines.push(" \u2514\u2500\u2500 ".concat(dep));
914
+ }
915
+ return [2 /*return*/, { content: [{ type: "text", text: lines.join("\n") }] }];
916
+ }
917
+ _w.label = 9;
918
+ case 9: throw new Error("Unknown tool: ".concat(name));
919
+ case 10: return [3 /*break*/, 12];
920
+ case 11:
921
+ error_1 = _w.sent();
922
+ return [2 /*return*/, {
923
+ content: [{ type: "text", text: "Error: ".concat(error_1 instanceof Error ? error_1.message : String(error_1)) }],
924
+ isError: true,
925
+ }];
926
+ case 12: return [2 /*return*/];
927
+ }
928
+ });
929
+ }); });
930
+ // Start server
931
+ function main() {
932
+ return __awaiter(this, void 0, void 0, function () {
933
+ var transport;
934
+ var _this = this;
935
+ return __generator(this, function (_a) {
936
+ switch (_a.label) {
937
+ case 0:
938
+ // Set up error handling
939
+ server.onerror = function (error) { return console.error('[MCP] Error:', error); };
940
+ // Signal handlers for graceful shutdown
941
+ process.on('SIGINT', function () { return __awaiter(_this, void 0, void 0, function () {
942
+ return __generator(this, function (_a) {
943
+ switch (_a.label) {
944
+ case 0:
945
+ console.error('[MCP] Shutting down...');
946
+ return [4 /*yield*/, server.close()];
947
+ case 1:
948
+ _a.sent();
949
+ process.exit(0);
950
+ return [2 /*return*/];
951
+ }
952
+ });
953
+ }); });
954
+ process.on('SIGTERM', function () { return __awaiter(_this, void 0, void 0, function () {
955
+ return __generator(this, function (_a) {
956
+ switch (_a.label) {
957
+ case 0:
958
+ console.error('[MCP] Shutting down...');
959
+ return [4 /*yield*/, server.close()];
960
+ case 1:
961
+ _a.sent();
962
+ process.exit(0);
963
+ return [2 /*return*/];
964
+ }
965
+ });
966
+ }); });
967
+ transport = new stdio_js_1.StdioServerTransport();
968
+ return [4 /*yield*/, server.connect(transport)];
969
+ case 1:
970
+ _a.sent();
971
+ // Log to stderr (doesn't interfere with JSON-RPC)
972
+ console.error('[MCP] @mcp/dependency-graph server running on stdio');
973
+ // Keep stdin open for requests
974
+ process.stdin.resume();
975
+ return [2 /*return*/];
976
+ }
977
+ });
978
+ });
979
+ }
980
+ main().catch(function (error) {
981
+ console.error('[MCP] Fatal error:', error);
982
+ process.exit(1);
983
+ });