@syke1/mcp-server 1.4.22 → 1.5.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/README.md CHANGED
@@ -89,16 +89,16 @@ SYKE auto-detects your project language and builds the dependency graph on start
89
89
 
90
90
  ### 8 MCP Tools
91
91
 
92
- | Tool | Description |
93
- |------|-------------|
94
- | `gate_build` | **Mandatory pre-build check.** Returns PASS/WARN/FAIL verdict before any build or deploy. |
95
- | `analyze_impact` | Shows direct and transitive dependents when a file is modified. |
96
- | `check_safe` | Quick one-line safety verdict: HIGH/MEDIUM/LOW/NONE risk. |
97
- | `get_dependencies` | Lists internal imports (forward dependencies) of a file. |
98
- | `get_hub_files` | Ranks files by how many other files depend on them. |
99
- | `refresh_graph` | Re-scans all source files and rebuilds the dependency graph. |
100
- | `ai_analyze` | **Pro** AI semantic analysis (Gemini/OpenAI/Claude) of a file and its dependents. |
101
- | `check_warnings` | Real-time monitoring alerts for file changes that may break dependents. |
92
+ | Tool | Tier | Description |
93
+ |------|------|-------------|
94
+ | `gate_build` | Free | **Mandatory pre-build check.** Returns PASS/WARN/FAIL verdict before any build or deploy. |
95
+ | `check_safe` | Free | Quick one-line safety verdict: HIGH/MEDIUM/LOW/NONE risk. |
96
+ | `get_dependencies` | Free | Lists internal imports (forward dependencies) of a file. |
97
+ | `analyze_impact` | **Pro** | Full impact analysis with SCC, risk scoring, and git coupling. |
98
+ | `get_hub_files` | **Pro** | Ranks files by PageRank importance score. |
99
+ | `refresh_graph` | **Pro** | Re-scans all source files and rebuilds the dependency graph. |
100
+ | `ai_analyze` | **Pro** | AI semantic analysis (Gemini/OpenAI/Claude) of a file and its dependents. |
101
+ | `check_warnings` | **Pro** | Real-time monitoring alerts for file changes that may break dependents. |
102
102
 
103
103
  ### Multi-AI Provider Support
104
104
 
@@ -254,15 +254,14 @@ You (developer) AI Agent SYKE
254
254
  ## Pricing
255
255
 
256
256
  ### Free — $0 forever
257
- - 50 files per project
258
- - Core tools (gate_build, analyze_impact, check_safe, get_dependencies, refresh_graph)
257
+ - 3 tools: `gate_build`, `check_safe`, `get_dependencies`
258
+ - 50 files per project, single project
259
259
  - 8 language support
260
260
 
261
261
  ### Pro — $12/mo (or $99/yr, save 31%)
262
- - Unlimited files per project
263
- - All tools including get_hub_files, ai_analyze, check_warnings
264
- - Real-time cascade monitoring (SSE)
265
- - Cycle detection & simulation
262
+ - All 8 tools with advanced algorithms (SCC, PageRank, Risk Scoring, Git Coupling)
263
+ - Unlimited files, multi-project support
264
+ - Real-time cascade monitoring + web dashboard
266
265
  - AI semantic analysis (BYOK — Gemini, OpenAI, or Claude)
267
266
  - Priority support
268
267
 
package/dist/graph.d.ts CHANGED
@@ -14,6 +14,6 @@ export interface DependencyGraph {
14
14
  /** PageRank importance scores — computed after graph build */
15
15
  pageRank?: PageRankResult;
16
16
  }
17
- export declare function buildGraph(projectRoot: string, packageName?: string): DependencyGraph;
18
- export declare function getGraph(projectRoot: string, packageName?: string): DependencyGraph;
19
- export declare function refreshGraph(projectRoot: string, packageName?: string): DependencyGraph;
17
+ export declare function buildGraph(projectRoot: string, packageName?: string, maxFiles?: number): DependencyGraph;
18
+ export declare function getGraph(projectRoot: string, packageName?: string, maxFiles?: number): DependencyGraph;
19
+ export declare function rebuildGraph(projectRoot: string, packageName?: string, maxFiles?: number): DependencyGraph;
package/dist/graph.js CHANGED
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.buildGraph = buildGraph;
37
37
  exports.getGraph = getGraph;
38
- exports.refreshGraph = refreshGraph;
38
+ exports.rebuildGraph = rebuildGraph;
39
39
  const path = __importStar(require("path"));
40
40
  const plugin_1 = require("./languages/plugin");
41
41
  const typescript_1 = require("./languages/typescript");
@@ -44,13 +44,15 @@ const risk_scorer_1 = require("./scoring/risk-scorer");
44
44
  const pagerank_1 = require("./scoring/pagerank");
45
45
  const memo_cache_1 = require("./graph/memo-cache");
46
46
  let cachedGraph = null;
47
- function buildGraph(projectRoot, packageName) {
47
+ function buildGraph(projectRoot, packageName, maxFiles) {
48
48
  const detectedPlugins = (0, plugin_1.detectLanguages)(projectRoot);
49
49
  const languages = detectedPlugins.map(p => p.id);
50
50
  const forward = new Map();
51
51
  const reverse = new Map();
52
52
  const files = new Set();
53
53
  const allSourceDirs = [];
54
+ let totalDiscovered = 0;
55
+ let fileLimitHit = false;
54
56
  for (const plugin of detectedPlugins) {
55
57
  const dirs = plugin.getSourceDirs(projectRoot);
56
58
  console.error(`[syke:debug] ${plugin.id} getSourceDirs(${projectRoot}) => ${dirs.length} dirs: ${dirs.join(", ")}`);
@@ -59,12 +61,19 @@ function buildGraph(projectRoot, packageName) {
59
61
  allSourceDirs.push(dir);
60
62
  const sourceFiles = plugin.discoverFiles(dir);
61
63
  console.error(`[syke:debug] ${plugin.id} discoverFiles(${dir}) => ${sourceFiles.length} files`);
64
+ totalDiscovered += sourceFiles.length;
62
65
  for (const f of sourceFiles) {
66
+ if (maxFiles && files.size >= maxFiles) {
67
+ fileLimitHit = true;
68
+ break;
69
+ }
63
70
  files.add(f);
64
71
  if (!forward.has(f))
65
72
  forward.set(f, []);
66
73
  }
67
74
  for (const f of sourceFiles) {
75
+ if (!files.has(f))
76
+ continue;
68
77
  const imports = plugin.parseImports(f, projectRoot, dir);
69
78
  const validImports = [];
70
79
  for (const imp of imports) {
@@ -79,6 +88,9 @@ function buildGraph(projectRoot, packageName) {
79
88
  }
80
89
  }
81
90
  }
91
+ if (fileLimitHit) {
92
+ console.error(`[syke] Free tier: loaded ${files.size}/${totalDiscovered} files (limit: ${maxFiles}). Upgrade to Pro for unlimited.`);
93
+ }
82
94
  const sourceDir = allSourceDirs[0] || path.join(projectRoot, "src");
83
95
  const graph = {
84
96
  forward,
@@ -109,17 +121,17 @@ function countEdges(forward) {
109
121
  }
110
122
  return count;
111
123
  }
112
- function getGraph(projectRoot, packageName) {
124
+ function getGraph(projectRoot, packageName, maxFiles) {
113
125
  if (cachedGraph && cachedGraph.projectRoot === projectRoot) {
114
126
  return cachedGraph;
115
127
  }
116
- return buildGraph(projectRoot, packageName);
128
+ return buildGraph(projectRoot, packageName, maxFiles);
117
129
  }
118
- function refreshGraph(projectRoot, packageName) {
130
+ function rebuildGraph(projectRoot, packageName, maxFiles) {
119
131
  cachedGraph = null;
120
132
  (0, typescript_1.clearAliasCache)();
121
133
  (0, risk_scorer_1.invalidateProjectMetrics)();
122
134
  (0, pagerank_1.invalidatePageRank)();
123
135
  (0, memo_cache_1.resetMemoCache)();
124
- return buildGraph(projectRoot, packageName);
136
+ return buildGraph(projectRoot, packageName, maxFiles);
125
137
  }
package/dist/index.js CHANGED
@@ -81,6 +81,9 @@ const FREE_MAX_FILES = 50;
81
81
  function isPro() {
82
82
  return licenseStatus.plan === "pro" || licenseStatus.plan === "pro_trial";
83
83
  }
84
+ function getMaxFiles() {
85
+ return isPro() ? undefined : FREE_MAX_FILES;
86
+ }
84
87
  /**
85
88
  * Check if a resolved file path is within the free tier limit.
86
89
  * Free set = first 50 files sorted alphabetically by relative path.
@@ -150,7 +153,7 @@ async function main() {
150
153
  },
151
154
  {
152
155
  name: "analyze_impact",
153
- description: "Analyze which files are impacted when a given file is modified. Returns direct and transitive dependents with risk level.",
156
+ description: "[PRO] Analyze which files are impacted when a given file is modified. Returns direct and transitive dependents with risk level.",
154
157
  inputSchema: {
155
158
  type: "object",
156
159
  properties: {
@@ -186,7 +189,7 @@ async function main() {
186
189
  },
187
190
  {
188
191
  name: "get_hub_files",
189
- description: "Rank files by how many other files depend on them. High-hub files are risky to modify.",
192
+ description: "[PRO] Rank files by how many other files depend on them (PageRank). High-hub files are risky to modify.",
190
193
  inputSchema: {
191
194
  type: "object",
192
195
  properties: {
@@ -199,7 +202,7 @@ async function main() {
199
202
  },
200
203
  {
201
204
  name: "refresh_graph",
202
- description: "Re-scan all source files and rebuild the dependency graph. Use after adding/removing files.",
205
+ description: "[PRO] Re-scan all source files and rebuild the dependency graph. Use after adding/removing files.",
203
206
  inputSchema: {
204
207
  type: "object",
205
208
  properties: {},
@@ -207,7 +210,7 @@ async function main() {
207
210
  },
208
211
  {
209
212
  name: "ai_analyze",
210
- description: "Use AI (Gemini/OpenAI/Claude) to perform semantic analysis on a file. Reads the file's source code and its dependents to explain what might break when modified and how to safely make changes.",
213
+ description: "[PRO] Use AI (Gemini/OpenAI/Claude) to perform semantic analysis on a file. Reads the file's source code and its dependents to explain what might break when modified and how to safely make changes.",
211
214
  inputSchema: {
212
215
  type: "object",
213
216
  properties: {
@@ -221,7 +224,7 @@ async function main() {
221
224
  },
222
225
  {
223
226
  name: "check_warnings",
224
- description: "Check for unresolved warnings from SYKE's real-time monitoring. Returns warnings about file changes that may have broken dependents. Use this AFTER modifying files to see if SYKE caught any issues you may have missed. Pass acknowledge=true to clear warnings after reading them.",
227
+ description: "[PRO] Check for unresolved warnings from SYKE's real-time monitoring. Returns warnings about file changes that may have broken dependents. Use this AFTER modifying files to see if SYKE caught any issues you may have missed. Pass acknowledge=true to clear warnings after reading them.",
225
228
  inputSchema: {
226
229
  type: "object",
227
230
  properties: {
@@ -249,7 +252,7 @@ async function main() {
249
252
  const { name, arguments: args } = request.params;
250
253
  switch (name) {
251
254
  case "gate_build": {
252
- const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
255
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName, getMaxFiles());
253
256
  const files = args.files?.map((f) => resolveFilePath(f, currentProjectRoot, graph.sourceDir));
254
257
  const result = await (0, gate_build_1.gateCheck)(graph, files);
255
258
  return {
@@ -260,8 +263,11 @@ async function main() {
260
263
  };
261
264
  }
262
265
  case "analyze_impact": {
266
+ if (!isPro()) {
267
+ return { content: [{ type: "text", text: getProToolError("analyze_impact") }] };
268
+ }
263
269
  const file = args.file;
264
- const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
270
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName, getMaxFiles());
265
271
  const resolved = resolveFilePath(file, currentProjectRoot, graph.sourceDir);
266
272
  if (!graph.files.has(resolved)) {
267
273
  return {
@@ -273,9 +279,6 @@ async function main() {
273
279
  ],
274
280
  };
275
281
  }
276
- if (!isFileInFreeSet(resolved, graph)) {
277
- return { content: [{ type: "text", text: PRO_UPGRADE_MSG }] };
278
- }
279
282
  const result = await (0, analyze_impact_1.analyzeImpact)(resolved, graph, { includeRiskScore: true, includeCoupling: true });
280
283
  const cachedTag = result.fromCache ? " (cached)" : "";
281
284
  const lines = [
@@ -360,7 +363,7 @@ async function main() {
360
363
  }
361
364
  case "check_safe": {
362
365
  const file = args.file;
363
- const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
366
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName, getMaxFiles());
364
367
  const resolved = resolveFilePath(file, currentProjectRoot, graph.sourceDir);
365
368
  if (!graph.files.has(resolved)) {
366
369
  return {
@@ -412,7 +415,7 @@ async function main() {
412
415
  }
413
416
  case "get_dependencies": {
414
417
  const file = args.file;
415
- const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
418
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName, getMaxFiles());
416
419
  const resolved = resolveFilePath(file, currentProjectRoot, graph.sourceDir);
417
420
  if (!graph.files.has(resolved)) {
418
421
  return {
@@ -453,7 +456,7 @@ async function main() {
453
456
  };
454
457
  }
455
458
  const requestedN = args.top_n || 10;
456
- const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
459
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName, getMaxFiles());
457
460
  const hubs = (0, analyze_impact_1.getHubFiles)(graph, requestedN);
458
461
  // If PageRank is available, build enriched entries sorted by PageRank
459
462
  const pageRankAvailable = !!graph.pageRank;
@@ -517,7 +520,10 @@ async function main() {
517
520
  return { content: [{ type: "text", text: lines.join("\n") }] };
518
521
  }
519
522
  case "refresh_graph": {
520
- const graph = (0, graph_1.refreshGraph)(currentProjectRoot, currentPackageName);
523
+ if (!isPro()) {
524
+ return { content: [{ type: "text", text: getProToolError("refresh_graph") }] };
525
+ }
526
+ const graph = (0, graph_1.rebuildGraph)(currentProjectRoot, currentPackageName, getMaxFiles());
521
527
  (0, change_coupling_1.invalidateCouplingCache)();
522
528
  const cacheStats = (0, analyze_impact_1.getImpactMemoCache)().stats();
523
529
  return {
@@ -538,7 +544,7 @@ async function main() {
538
544
  };
539
545
  }
540
546
  const file = args.file;
541
- const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
547
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName, getMaxFiles());
542
548
  const resolved = resolveFilePath(file, currentProjectRoot, graph.sourceDir);
543
549
  if (!graph.files.has(resolved)) {
544
550
  return {
@@ -642,7 +648,7 @@ async function main() {
642
648
  console.error(`[syke] Pro activated for: ${licenseStatus.email || "unknown"}`);
643
649
  }
644
650
  else {
645
- console.error(`[syke] Free tier: ${FREE_MAX_FILES} file limit, ai_analyze/get_hub_files/check_warnings disabled`);
651
+ console.error(`[syke] Free tier: ${FREE_MAX_FILES} file limit, 3 tools (gate_build, check_safe, get_dependencies)`);
646
652
  console.error(`[syke] Upgrade at https://syke.cloud/dashboard/`);
647
653
  }
648
654
  let fileCache = null;
@@ -651,12 +657,7 @@ async function main() {
651
657
  console.error(`[syke] Project root: ${currentProjectRoot}`);
652
658
  console.error(`[syke] Detected languages: ${detectedLangs}`);
653
659
  console.error(`[syke] Package name: ${currentPackageName}`);
654
- const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
655
- // Free tier: warn if over file limit
656
- if (licenseStatus.plan === "free" && graph.files.size > FREE_MAX_FILES) {
657
- console.error(`[syke] WARNING: Free tier limited to ${FREE_MAX_FILES} files. Your project has ${graph.files.size} files.`);
658
- console.error(`[syke] Only the first ${FREE_MAX_FILES} files will be analyzed. Upgrade to Pro for unlimited files.`);
659
- }
660
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName, getMaxFiles());
660
661
  // Initialize file cache (load ALL source files into memory)
661
662
  fileCache = new file_cache_1.FileCache(currentProjectRoot);
662
663
  fileCache.initialize();
@@ -676,7 +677,7 @@ async function main() {
676
677
  fileCache = new file_cache_1.FileCache(newRoot);
677
678
  fileCache.initialize();
678
679
  // Rebuild graph
679
- const graph = (0, graph_1.refreshGraph)(newRoot, currentPackageName);
680
+ const graph = (0, graph_1.rebuildGraph)(newRoot, currentPackageName, getMaxFiles());
680
681
  // Enable incremental updates on the new cache
681
682
  fileCache.setGraph(graph);
682
683
  fileCache.startWatching();
@@ -701,7 +702,7 @@ async function main() {
701
702
  }
702
703
  // Start Express web server with file cache for SSE (only if project detected)
703
704
  if (currentProjectRoot) {
704
- const { app: webApp, setFileCache: setWebFileCache } = (0, server_1.createWebServer)(() => (0, graph_1.getGraph)(currentProjectRoot, currentPackageName), fileCache, switchProject, () => currentProjectRoot, () => currentPackageName, () => licenseStatus, () => !!(0, provider_1.getAIProvider)(), async (key) => {
705
+ const { app: webApp, setFileCache: setWebFileCache } = (0, server_1.createWebServer)(() => (0, graph_1.getGraph)(currentProjectRoot, currentPackageName, getMaxFiles()), fileCache, switchProject, () => currentProjectRoot, () => currentPackageName, () => licenseStatus, () => !!(0, provider_1.getAIProvider)(), async (key) => {
705
706
  // Stop existing heartbeat/session
706
707
  await (0, validator_1.stopAndDeactivate)();
707
708
  if (key && (key.startsWith("SYKE-") || key.startsWith("FOUNDING-"))) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syke1/mcp-server",
3
- "version": "1.4.22",
3
+ "version": "1.5.0",
4
4
  "mcpName": "io.github.khalomsky/syke",
5
5
  "description": "AI code impact analysis MCP server — dependency graphs, cascade detection, and a mandatory build gate for AI coding agents",
6
6
  "main": "dist/index.js",