@syke1/mcp-server 1.7.0 → 1.8.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.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Project Scanner — Cortex-only tool.
3
+ * Scans the entire project dependency graph and generates a comprehensive
4
+ * onboarding document using AI (BYOK).
5
+ */
6
+ import { DependencyGraph } from "../graph";
7
+ export declare function scanProject(graph: DependencyGraph): Promise<string>;
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ /**
3
+ * Project Scanner — Cortex-only tool.
4
+ * Scans the entire project dependency graph and generates a comprehensive
5
+ * onboarding document using AI (BYOK).
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.scanProject = scanProject;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const provider_1 = require("./provider");
45
+ const context_extractor_1 = require("./context-extractor");
46
+ // ── Config file names to look for ──
47
+ const CONFIG_FILE_NAMES = [
48
+ "package.json", "tsconfig.json", "tsconfig.base.json",
49
+ "Cargo.toml", "go.mod", "go.sum",
50
+ "pubspec.yaml", "build.gradle", "pom.xml",
51
+ "Gemfile", "requirements.txt", "pyproject.toml", "setup.py",
52
+ "Makefile", "CMakeLists.txt",
53
+ "docker-compose.yml", "Dockerfile",
54
+ ".env.example",
55
+ ];
56
+ const MAX_CONFIG_LINES = 200;
57
+ // ── Core ──
58
+ async function scanProject(graph) {
59
+ const provider = (0, provider_1.getAIProvider)();
60
+ if (!provider) {
61
+ return "scan_project requires an AI API key.\n\nSet one of: GEMINI_KEY, OPENAI_KEY, or ANTHROPIC_KEY.";
62
+ }
63
+ const ctx = collectProjectContext(graph);
64
+ const systemPrompt = buildScanSystemPrompt();
65
+ const userPrompt = buildScanUserPrompt(ctx);
66
+ try {
67
+ return await provider.analyze(systemPrompt, userPrompt);
68
+ }
69
+ catch (err) {
70
+ return `AI analysis error: ${err.message || err}`;
71
+ }
72
+ }
73
+ // ── Context Collection ──
74
+ function collectProjectContext(graph) {
75
+ const projectRoot = graph.projectRoot;
76
+ // 1. Hub files — sorted by reverse dependency count
77
+ const hubFiles = getHubFiles(graph, 10);
78
+ // 2. Entry points — files with 0 reverse dependencies
79
+ const entryPoints = getEntryPoints(graph);
80
+ // 3. Circular dependencies (SCC clusters with size > 1)
81
+ const circularDependencies = getCircularDeps(graph);
82
+ // 4. Directory structure (depth-limited tree)
83
+ const directoryStructure = buildDirectoryTree(graph);
84
+ // 5. Config files
85
+ const configFiles = readConfigFiles(projectRoot);
86
+ // 6. Key file signatures (top 5 hub files)
87
+ const keyFileSignatures = buildKeySignatures(hubFiles.slice(0, 5), graph);
88
+ return {
89
+ projectRoot,
90
+ languages: graph.languages,
91
+ totalFiles: graph.files.size,
92
+ directoryStructure,
93
+ hubFiles,
94
+ entryPoints,
95
+ circularDependencies,
96
+ configFiles,
97
+ keyFileSignatures,
98
+ };
99
+ }
100
+ function getHubFiles(graph, topN) {
101
+ const counts = [];
102
+ for (const file of graph.files) {
103
+ const revDeps = graph.reverse.get(file) || [];
104
+ counts.push({ path: file, count: revDeps.length });
105
+ }
106
+ counts.sort((a, b) => b.count - a.count);
107
+ return counts.slice(0, topN).map((c) => ({
108
+ path: c.path,
109
+ relativePath: path.relative(graph.sourceDir, c.path).replace(/\\/g, "/"),
110
+ dependentCount: c.count,
111
+ }));
112
+ }
113
+ function getEntryPoints(graph) {
114
+ const entries = [];
115
+ for (const file of graph.files) {
116
+ const revDeps = graph.reverse.get(file) || [];
117
+ if (revDeps.length === 0) {
118
+ entries.push(path.relative(graph.sourceDir, file).replace(/\\/g, "/"));
119
+ }
120
+ }
121
+ return entries.slice(0, 20); // cap at 20
122
+ }
123
+ function getCircularDeps(graph) {
124
+ if (!graph.scc)
125
+ return [];
126
+ return graph.scc.components
127
+ .filter((c) => c.length > 1)
128
+ .map((cluster) => cluster.map((f) => path.relative(graph.sourceDir, f).replace(/\\/g, "/")));
129
+ }
130
+ function buildDirectoryTree(graph) {
131
+ // Build a set of relative directory paths from file list
132
+ const dirs = new Map(); // dir -> file count
133
+ for (const file of graph.files) {
134
+ const rel = path.relative(graph.sourceDir, file).replace(/\\/g, "/");
135
+ const parts = rel.split("/");
136
+ // Count files per top-level and second-level directory
137
+ for (let depth = 1; depth <= Math.min(parts.length - 1, 3); depth++) {
138
+ const dirPath = parts.slice(0, depth).join("/");
139
+ dirs.set(dirPath, (dirs.get(dirPath) || 0) + 1);
140
+ }
141
+ }
142
+ // Sort and format as tree
143
+ const sorted = [...dirs.entries()].sort(([a], [b]) => a.localeCompare(b));
144
+ const lines = [];
145
+ for (const [dirPath, count] of sorted) {
146
+ const depth = dirPath.split("/").length - 1;
147
+ const indent = " ".repeat(depth);
148
+ const name = dirPath.split("/").pop();
149
+ lines.push(`${indent}${name}/ (${count} files)`);
150
+ }
151
+ return lines.join("\n") || "(flat structure)";
152
+ }
153
+ function readConfigFiles(projectRoot) {
154
+ const configs = [];
155
+ for (const name of CONFIG_FILE_NAMES) {
156
+ const filePath = path.join(projectRoot, name);
157
+ try {
158
+ if (fs.existsSync(filePath)) {
159
+ const content = fs.readFileSync(filePath, "utf-8");
160
+ const lines = content.split("\n");
161
+ const truncated = lines.length > MAX_CONFIG_LINES
162
+ ? lines.slice(0, MAX_CONFIG_LINES).join("\n") + `\n... (${lines.length - MAX_CONFIG_LINES} more lines)`
163
+ : content;
164
+ configs.push({ path: filePath, name, content: truncated });
165
+ }
166
+ }
167
+ catch {
168
+ // skip unreadable files
169
+ }
170
+ }
171
+ return configs;
172
+ }
173
+ function buildKeySignatures(hubFiles, graph) {
174
+ const parts = [];
175
+ for (const hub of hubFiles) {
176
+ try {
177
+ const content = fs.readFileSync(hub.path, "utf-8");
178
+ const lang = graph.languages[0] || "typescript";
179
+ const summary = (0, context_extractor_1.buildSmartContext)(content, lang, 80);
180
+ parts.push(`### ${hub.relativePath} (${hub.dependentCount} dependents)\n${summary}`);
181
+ }
182
+ catch {
183
+ parts.push(`### ${hub.relativePath} (${hub.dependentCount} dependents)\n(unable to read)`);
184
+ }
185
+ }
186
+ return parts.join("\n\n");
187
+ }
188
+ // ── AI Prompts ──
189
+ function buildScanSystemPrompt() {
190
+ return `You are a senior software architect generating a comprehensive codebase onboarding document.
191
+ Based on the structural analysis data provided (dependency graph metrics, hub file rankings, circular dependencies, file signatures, and config files), generate a document following this exact structure:
192
+
193
+ ## 1. Project Overview
194
+ Brief description of what this project does, its tech stack, and key technologies.
195
+
196
+ ## 2. Architecture Overview
197
+ High-level architecture pattern (monolith, microservices, layered, etc.) and how components connect.
198
+
199
+ ## 3. Repository Structure
200
+ Explain the directory layout and what each major directory contains.
201
+
202
+ ## 4. Module / Component Map
203
+ Map out the key modules, their responsibilities, and how they relate to each other.
204
+
205
+ ## 5. Key Concepts & Domain Model
206
+ Core domain concepts and abstractions that a new developer must understand.
207
+
208
+ ## 6. Development Workflow
209
+ How to build, test, and run the project based on config files found.
210
+
211
+ ## 7. Architectural Decisions
212
+ Notable patterns, frameworks, or design choices evident from the code structure.
213
+
214
+ ## 8. Cross-Cutting Concerns
215
+ Shared utilities, common patterns, error handling, logging approaches found.
216
+
217
+ ## 9. Danger Zones & Common Tasks
218
+ High-risk files (hub files with many dependents), circular dependencies to be aware of, and safe patterns for common modifications.
219
+
220
+ ## 10. Quick Reference
221
+ Entry points, key file paths, and the most important files a new team member should read first.
222
+
223
+ Use the dependency graph data, hub file rankings, circular dependencies, entry points, and file signatures to make your analysis accurate and specific to THIS project.
224
+ Be concise but comprehensive. Output in Markdown. Do not include generic advice — only project-specific insights.`;
225
+ }
226
+ function buildScanUserPrompt(ctx) {
227
+ const sections = [];
228
+ sections.push(`# Project Scan Data`);
229
+ sections.push(`- **Root:** ${ctx.projectRoot}`);
230
+ sections.push(`- **Languages:** ${ctx.languages.join(", ")}`);
231
+ sections.push(`- **Total files in graph:** ${ctx.totalFiles}`);
232
+ sections.push(`\n## Directory Structure\n\`\`\`\n${ctx.directoryStructure}\n\`\`\``);
233
+ sections.push(`\n## Hub Files (Top ${ctx.hubFiles.length} by dependent count)`);
234
+ for (const h of ctx.hubFiles) {
235
+ sections.push(`- **${h.relativePath}** — ${h.dependentCount} dependents`);
236
+ }
237
+ sections.push(`\n## Entry Points (${ctx.entryPoints.length} files with 0 reverse deps)`);
238
+ for (const e of ctx.entryPoints) {
239
+ sections.push(`- ${e}`);
240
+ }
241
+ if (ctx.circularDependencies.length > 0) {
242
+ sections.push(`\n## Circular Dependencies (${ctx.circularDependencies.length} clusters)`);
243
+ for (let i = 0; i < ctx.circularDependencies.length; i++) {
244
+ sections.push(`### Cluster ${i + 1} (${ctx.circularDependencies[i].length} files)`);
245
+ for (const f of ctx.circularDependencies[i]) {
246
+ sections.push(`- ${f}`);
247
+ }
248
+ }
249
+ }
250
+ else {
251
+ sections.push(`\n## Circular Dependencies\nNone detected.`);
252
+ }
253
+ if (ctx.configFiles.length > 0) {
254
+ sections.push(`\n## Config Files`);
255
+ for (const cfg of ctx.configFiles) {
256
+ sections.push(`### ${cfg.name}\n\`\`\`\n${cfg.content}\n\`\`\``);
257
+ }
258
+ }
259
+ if (ctx.keyFileSignatures) {
260
+ sections.push(`\n## Key File Signatures (Top Hub Files)\n${ctx.keyFileSignatures}`);
261
+ }
262
+ return sections.join("\n");
263
+ }
package/dist/index.js CHANGED
@@ -82,7 +82,21 @@ function isPro() {
82
82
  licenseStatus = { plan: "free", source: "default" };
83
83
  return false;
84
84
  }
85
- return licenseStatus.plan === "pro" || licenseStatus.plan === "pro_trial";
85
+ return ["pro", "pro_trial", "cortex"].includes(licenseStatus.plan);
86
+ }
87
+ function isCortex() {
88
+ if (!(0, validator_1._verifyStatus)(licenseStatus)) {
89
+ licenseStatus = { plan: "free", source: "default" };
90
+ return false;
91
+ }
92
+ return licenseStatus.plan === "cortex";
93
+ }
94
+ function getCortexToolError(toolName) {
95
+ if (licenseStatus.error)
96
+ return `${toolName}: ${licenseStatus.error}`;
97
+ if (isPro())
98
+ return `${toolName} requires SYKE Cortex. Upgrade at https://syke.cloud/dashboard/`;
99
+ return `${toolName} requires SYKE Cortex. Sign up at https://syke.cloud`;
86
100
  }
87
101
  function getMaxFiles() {
88
102
  return isPro() ? undefined : FREE_MAX_FILES;
@@ -133,7 +147,7 @@ async function main() {
133
147
  };
134
148
  process.on("SIGINT", shutdown);
135
149
  process.on("SIGTERM", shutdown);
136
- const server = new index_js_1.Server({ name: "syke", version: "1.7.0" }, { capabilities: { tools: {} } });
150
+ const server = new index_js_1.Server({ name: "syke", version: "1.8.0" }, { capabilities: { tools: {} } });
137
151
  // List tools
138
152
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
139
153
  tools: [
@@ -213,7 +227,7 @@ async function main() {
213
227
  },
214
228
  {
215
229
  name: "ai_analyze",
216
- 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.",
230
+ description: "[CORTEX] 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.",
217
231
  inputSchema: {
218
232
  type: "object",
219
233
  properties: {
@@ -238,6 +252,16 @@ async function main() {
238
252
  },
239
253
  },
240
254
  },
255
+ {
256
+ name: "scan_project",
257
+ description: "[CORTEX] Scan the entire project and generate a comprehensive onboarding document. "
258
+ + "Analyzes architecture, key files, dependencies, and patterns to help new team members "
259
+ + "understand the codebase quickly. Requires AI API key (BYOK).",
260
+ inputSchema: {
261
+ type: "object",
262
+ properties: {},
263
+ },
264
+ },
241
265
  ],
242
266
  }));
243
267
  // Dashboard URL footer — shown only on the first successful tool call
@@ -491,11 +515,16 @@ async function main() {
491
515
  };
492
516
  }
493
517
  case "ai_analyze": {
494
- // BYOK: allow if user has their own AI key, even on Free plan
518
+ // Cortex-only tool
519
+ if (!isCortex()) {
520
+ return {
521
+ content: [{ type: "text", text: getCortexToolError("ai_analyze") }],
522
+ };
523
+ }
495
524
  const hasAIKey = !!(0, provider_1.getAIProvider)();
496
- if (!isPro() && !hasAIKey) {
525
+ if (!hasAIKey) {
497
526
  return {
498
- content: [{ type: "text", text: getProToolError("ai_analyze") + "\n\nOr set GEMINI_KEY / OPENAI_KEY / ANTHROPIC_KEY to use ai_analyze with your own API key." }],
527
+ content: [{ type: "text", text: `ai_analyze requires an AI API key.\n\nSet one of: GEMINI_KEY, OPENAI_KEY, or ANTHROPIC_KEY in your environment.\nSee: https://syke.cloud/docs/ai-analyze` }],
499
528
  };
500
529
  }
501
530
  const file = args.file;
@@ -511,37 +540,12 @@ async function main() {
511
540
  ],
512
541
  };
513
542
  }
514
- if (!isFileInFreeSet(resolved, graph)) {
515
- return { content: [{ type: "text", text: PRO_UPGRADE_MSG }] };
516
- }
517
- // BYOK path: user has their own AI key → use local AI analysis
518
- if (hasAIKey) {
519
- const { analyzeImpact: localAnalyzeImpact } = await Promise.resolve().then(() => __importStar(require("./tools/analyze-impact")));
520
- const impactResult = await localAnalyzeImpact(resolved, graph);
521
- const aiResult = await (0, analyzer_1.analyzeWithAI)(resolved, impactResult, graph);
522
- let resultText = aiResult;
523
- if (!isPro() && graph.files.size > FREE_MAX_FILES) {
524
- resultText += `\n\n---\nFree tier: analyzing ${FREE_MAX_FILES}/${graph.files.size} files. Some dependencies may be missing. Upgrade to Pro for full analysis: https://syke.cloud/dashboard/`;
525
- }
526
- return {
527
- content: [{ type: "text", text: appendDashboardFooter(resultText) }],
528
- };
529
- }
530
- // Pro path: use server-side AI analysis (server's Gemini key)
531
- try {
532
- const fs = await Promise.resolve().then(() => __importStar(require("fs")));
533
- const fileContents = fs.readFileSync(resolved, "utf-8");
534
- const result = await (0, proxy_1.remoteAIAnalyze)(graph, resolved, fileContents);
535
- return {
536
- content: [{ type: "text", text: appendDashboardFooter(result.analysis) }],
537
- };
538
- }
539
- catch (err) {
540
- return {
541
- content: [{ type: "text", text: `ai_analyze failed: ${err.message || err}` }],
542
- isError: true,
543
- };
544
- }
543
+ const { analyzeImpact: localAnalyzeImpact } = await Promise.resolve().then(() => __importStar(require("./tools/analyze-impact")));
544
+ const impactResult = await localAnalyzeImpact(resolved, graph);
545
+ const aiResult = await (0, analyzer_1.analyzeWithAI)(resolved, impactResult, graph);
546
+ return {
547
+ content: [{ type: "text", text: appendDashboardFooter(aiResult) }],
548
+ };
545
549
  }
546
550
  case "check_warnings": {
547
551
  // Pro-only feature (real-time monitoring)
@@ -594,6 +598,26 @@ async function main() {
594
598
  content: [{ type: "text", text: appendDashboardFooter(lines.join("\n")) }],
595
599
  };
596
600
  }
601
+ case "scan_project": {
602
+ // Cortex-only tool
603
+ if (!isCortex()) {
604
+ return {
605
+ content: [{ type: "text", text: getCortexToolError("scan_project") }],
606
+ };
607
+ }
608
+ const hasAIKey = !!(0, provider_1.getAIProvider)();
609
+ if (!hasAIKey) {
610
+ return {
611
+ content: [{ type: "text", text: `scan_project requires an AI API key.\n\nSet one of: GEMINI_KEY, OPENAI_KEY, or ANTHROPIC_KEY in your environment.\nSee: https://syke.cloud/docs/ai-analyze` }],
612
+ };
613
+ }
614
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName, getMaxFiles());
615
+ const { scanProject } = await Promise.resolve().then(() => __importStar(require("./ai/project-scanner")));
616
+ const result = await scanProject(graph);
617
+ return {
618
+ content: [{ type: "text", text: appendDashboardFooter(result) }],
619
+ };
620
+ }
597
621
  default:
598
622
  return {
599
623
  content: [
@@ -604,7 +628,7 @@ async function main() {
604
628
  }
605
629
  });
606
630
  // Pre-warm the graph (skip if no project root — e.g. Smithery scan)
607
- console.error(`[syke] Starting SYKE MCP Server v1.7.0`);
631
+ console.error(`[syke] Starting SYKE MCP Server v1.8.0`);
608
632
  console.error(`[syke] License: ${licenseStatus.plan.toUpperCase()} (${licenseStatus.source})`);
609
633
  if (licenseStatus.expiresAt) {
610
634
  console.error(`[syke] Expires: ${licenseStatus.expiresAt}`);
@@ -774,7 +798,7 @@ main().catch((err) => {
774
798
  * See: https://smithery.ai/docs/deploy#sandbox-server
775
799
  */
776
800
  function createSandboxServer() {
777
- const sandboxServer = new index_js_1.Server({ name: "syke", version: "1.7.0" }, { capabilities: { tools: {} } });
801
+ const sandboxServer = new index_js_1.Server({ name: "syke", version: "1.8.0" }, { capabilities: { tools: {} } });
778
802
  sandboxServer.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
779
803
  tools: [
780
804
  {
@@ -809,7 +833,7 @@ function createSandboxServer() {
809
833
  },
810
834
  {
811
835
  name: "ai_analyze",
812
- description: "Pro: AI semantic analysis (Gemini/OpenAI/Claude) of a file and its dependents.",
836
+ description: "Cortex: AI semantic analysis (Gemini/OpenAI/Claude) of a file and its dependents.",
813
837
  inputSchema: { type: "object", properties: { file: { type: "string", description: "File to analyze" } }, required: ["file"] },
814
838
  },
815
839
  {
@@ -817,6 +841,11 @@ function createSandboxServer() {
817
841
  description: "Pro: Real-time monitoring alerts for file changes.",
818
842
  inputSchema: { type: "object", properties: {} },
819
843
  },
844
+ {
845
+ name: "scan_project",
846
+ description: "Cortex: Scan entire project and generate onboarding document.",
847
+ inputSchema: { type: "object", properties: {} },
848
+ },
820
849
  ],
821
850
  }));
822
851
  sandboxServer.setRequestHandler(types_js_1.CallToolRequestSchema, async () => ({
@@ -1,5 +1,5 @@
1
1
  export interface LicenseStatus {
2
- plan: "free" | "pro" | "pro_trial";
2
+ plan: "free" | "pro" | "pro_trial" | "cortex";
3
3
  email?: string;
4
4
  expiresAt?: string;
5
5
  source: "online" | "cache" | "default";
@@ -245,6 +245,17 @@ function clearLicenseCache() {
245
245
  // silently fail
246
246
  }
247
247
  }
248
+ /**
249
+ * Map server plan string to client plan type.
250
+ * Cortex plans → "cortex", trial → "pro_trial", everything else paid → "pro".
251
+ */
252
+ function mapPlan(serverPlan) {
253
+ if (serverPlan === "pro_trial")
254
+ return "pro_trial";
255
+ if (serverPlan === "cortex_monthly" || serverPlan === "cortex_annual" || serverPlan === "cortex")
256
+ return "cortex";
257
+ return "pro"; // pro_monthly, pro_annual, pro_founding, master, etc.
258
+ }
248
259
  /**
249
260
  * Main license validation — called on MCP server startup
250
261
  */
@@ -260,7 +271,7 @@ async function checkLicense() {
260
271
  if (cache && cache.valid && (now - cache.cachedAt) < CACHE_MAX_AGE) {
261
272
  // Cache is fresh — still start heartbeat with cached session
262
273
  startHeartbeat(key, getDeviceFingerprint());
263
- const cachedPlan = cache.plan === "pro_trial" ? "pro_trial" : "pro";
274
+ const cachedPlan = mapPlan(cache.plan || "");
264
275
  return {
265
276
  plan: cachedPlan,
266
277
  email: cache.email,
@@ -285,12 +296,13 @@ async function checkLicense() {
285
296
  cachedAt: now,
286
297
  });
287
298
  startHeartbeat(realKey, getDeviceFingerprint());
299
+ const foundingPlan = mapPlan(result.plan || "");
288
300
  return {
289
- plan: "pro",
301
+ plan: foundingPlan,
290
302
  email: result.email,
291
303
  expiresAt: result.expiresAt,
292
304
  source: "online",
293
- _t: _signStatus("pro", "online"),
305
+ _t: _signStatus(foundingPlan, "online"),
294
306
  };
295
307
  }
296
308
  if (!result.valid) {
@@ -308,7 +320,7 @@ async function checkLicense() {
308
320
  });
309
321
  // Start heartbeat
310
322
  startHeartbeat(key, getDeviceFingerprint());
311
- const onlinePlan = result.plan === "pro_trial" ? "pro_trial" : "pro";
323
+ const onlinePlan = mapPlan(result.plan || "");
312
324
  return {
313
325
  plan: onlinePlan,
314
326
  email: result.email,
@@ -329,7 +341,7 @@ async function checkLicense() {
329
341
  // Online validation failed — check if we have a grace-period cache
330
342
  if (cache && cache.valid && (now - cache.cachedAt) < CACHE_GRACE_PERIOD) {
331
343
  startHeartbeat(key, getDeviceFingerprint());
332
- const gracePlan = cache.plan === "pro_trial" ? "pro_trial" : "pro";
344
+ const gracePlan = mapPlan(cache.plan || "");
333
345
  return {
334
346
  plan: gracePlan,
335
347
  email: cache.email,
@@ -6,7 +6,7 @@
6
6
  * same output format, just executed on the server.
7
7
  */
8
8
  import { DependencyGraph } from "../graph";
9
- import { GraphBundle, RemoteAnalyzeImpactResponse, RemoteGetHubFilesResponse, RemoteAIAnalyzeResponse } from "./types";
9
+ import { GraphBundle, RemoteAnalyzeImpactResponse, RemoteGetHubFilesResponse } from "./types";
10
10
  /**
11
11
  * Serialize a DependencyGraph into a GraphBundle for transmission.
12
12
  * Converts absolute paths to relative paths (relative to sourceDir)
@@ -23,10 +23,6 @@ export declare function remoteAnalyzeImpact(graph: DependencyGraph, resolvedFile
23
23
  * Remote get_hub_files: sends graph to server for PageRank analysis.
24
24
  */
25
25
  export declare function remoteGetHubFiles(graph: DependencyGraph, topN?: number): Promise<RemoteGetHubFilesResponse>;
26
- /**
27
- * Remote ai_analyze: sends graph + file contents to server for AI analysis.
28
- */
29
- export declare function remoteAIAnalyze(graph: DependencyGraph, resolvedFilePath: string, fileContents: string): Promise<RemoteAIAnalyzeResponse>;
30
26
  /**
31
27
  * Remote license validation only (for refresh_graph, check_warnings).
32
28
  * Returns true if the license is valid Pro.
@@ -43,7 +43,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
43
43
  exports.serializeGraph = serializeGraph;
44
44
  exports.remoteAnalyzeImpact = remoteAnalyzeImpact;
45
45
  exports.remoteGetHubFiles = remoteGetHubFiles;
46
- exports.remoteAIAnalyze = remoteAIAnalyze;
47
46
  exports.remoteValidateLicense = remoteValidateLicense;
48
47
  const https = __importStar(require("https"));
49
48
  const path = __importStar(require("path"));
@@ -53,7 +52,6 @@ const BASE_URL = "https://us-central1-syke-cloud.cloudfunctions.net";
53
52
  const ENDPOINTS = {
54
53
  analyzeImpact: `${BASE_URL}/proAnalyzeImpact`,
55
54
  getHubFiles: `${BASE_URL}/proGetHubFiles`,
56
- aiAnalyze: `${BASE_URL}/proAIAnalyze`,
57
55
  };
58
56
  // ── Graph Serialization ──
59
57
  /**
@@ -173,26 +171,6 @@ async function remoteGetHubFiles(graph, topN = 10) {
173
171
  });
174
172
  return result;
175
173
  }
176
- /**
177
- * Remote ai_analyze: sends graph + file contents to server for AI analysis.
178
- */
179
- async function remoteAIAnalyze(graph, resolvedFilePath, fileContents) {
180
- const licenseKey = getLicenseKey();
181
- if (!licenseKey) {
182
- throw new Error("No license key configured");
183
- }
184
- const graphBundle = serializeGraph(graph);
185
- const targetFile = path.relative(graph.sourceDir, resolvedFilePath).replace(/\\/g, "/");
186
- const result = await postJSON(ENDPOINTS.aiAnalyze, {
187
- licenseKey,
188
- deviceId: getDeviceId(),
189
- graphBundle,
190
- targetFile,
191
- fileContents,
192
- languages: graph.languages,
193
- });
194
- return result;
195
- }
196
174
  /**
197
175
  * Remote license validation only (for refresh_graph, check_warnings).
198
176
  * Returns true if the license is valid Pro.
@@ -64,17 +64,6 @@ export interface RemoteGetHubFilesResponse {
64
64
  }>;
65
65
  totalFiles: number;
66
66
  }
67
- export interface RemoteAIAnalyzeRequest {
68
- licenseKey: string;
69
- deviceId: string;
70
- graphBundle: GraphBundle;
71
- targetFile: string;
72
- fileContents: string;
73
- languages: string[];
74
- }
75
- export interface RemoteAIAnalyzeResponse {
76
- analysis: string;
77
- }
78
67
  export interface RemoteError {
79
68
  error: string;
80
69
  code?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syke1/mcp-server",
3
- "version": "1.7.0",
3
+ "version": "1.8.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",