@skyramp/mcp 0.0.44 → 0.0.46

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.
Files changed (45) hide show
  1. package/build/index.js +57 -12
  2. package/build/prompts/code-reuse.js +1 -1
  3. package/build/prompts/driftAnalysisPrompt.js +159 -0
  4. package/build/prompts/modularization/ui-test-modularization.js +2 -0
  5. package/build/prompts/testGenerationPrompt.js +2 -2
  6. package/build/prompts/testHealthPrompt.js +82 -0
  7. package/build/services/AnalyticsService.js +86 -0
  8. package/build/services/DriftAnalysisService.js +928 -0
  9. package/build/services/ModularizationService.js +16 -1
  10. package/build/services/TestDiscoveryService.js +237 -0
  11. package/build/services/TestExecutionService.js +504 -0
  12. package/build/services/TestGenerationService.js +16 -2
  13. package/build/services/TestHealthService.js +656 -0
  14. package/build/tools/auth/loginTool.js +13 -3
  15. package/build/tools/auth/logoutTool.js +13 -3
  16. package/build/tools/code-refactor/codeReuseTool.js +46 -18
  17. package/build/tools/code-refactor/modularizationTool.js +44 -11
  18. package/build/tools/executeSkyrampTestTool.js +29 -125
  19. package/build/tools/fixErrorTool.js +38 -14
  20. package/build/tools/generate-tests/generateContractRestTool.js +8 -2
  21. package/build/tools/generate-tests/generateE2ERestTool.js +9 -3
  22. package/build/tools/generate-tests/generateFuzzRestTool.js +9 -3
  23. package/build/tools/generate-tests/generateIntegrationRestTool.js +8 -2
  24. package/build/tools/generate-tests/generateLoadRestTool.js +9 -3
  25. package/build/tools/generate-tests/generateScenarioRestTool.js +8 -2
  26. package/build/tools/generate-tests/generateSmokeRestTool.js +9 -3
  27. package/build/tools/generate-tests/generateUIRestTool.js +9 -3
  28. package/build/tools/test-maintenance/actionsTool.js +230 -0
  29. package/build/tools/test-maintenance/analyzeTestDriftTool.js +197 -0
  30. package/build/tools/test-maintenance/calculateHealthScoresTool.js +257 -0
  31. package/build/tools/test-maintenance/discoverTestsTool.js +143 -0
  32. package/build/tools/test-maintenance/executeBatchTestsTool.js +198 -0
  33. package/build/tools/test-maintenance/stateCleanupTool.js +153 -0
  34. package/build/tools/test-recommendation/analyzeRepositoryTool.js +27 -3
  35. package/build/tools/test-recommendation/mapTestsTool.js +9 -2
  36. package/build/tools/test-recommendation/recommendTestsTool.js +21 -5
  37. package/build/tools/trace/startTraceCollectionTool.js +18 -5
  38. package/build/tools/trace/stopTraceCollectionTool.js +28 -4
  39. package/build/types/TestAnalysis.js +1 -0
  40. package/build/types/TestDriftAnalysis.js +1 -0
  41. package/build/types/TestExecution.js +6 -0
  42. package/build/types/TestHealth.js +4 -0
  43. package/build/utils/AnalysisStateManager.js +240 -0
  44. package/build/utils/utils.test.js +25 -9
  45. package/package.json +6 -3
package/build/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { registerStartTraceCollectionPrompt } from "./prompts/startTraceCollectionPrompts.js";
5
+ import { registerTestHealthPrompt } from "./prompts/testHealthPrompt.js";
5
6
  import { registerTraceTool } from "./tools/trace/startTraceCollectionTool.js";
6
7
  import { registerTraceStopTool } from "./tools/trace/stopTraceCollectionTool.js";
7
8
  import { registerExecuteSkyrampTestTool } from "./tools/executeSkyrampTestTool.js";
@@ -23,9 +24,17 @@ import { registerRecommendTestsTool } from "./tools/test-recommendation/recommen
23
24
  import { registerModularizationTool } from "./tools/code-refactor/modularizationTool.js";
24
25
  import { registerCodeReuseTool } from "./tools/code-refactor/codeReuseTool.js";
25
26
  import { registerScenarioTestTool } from "./tools/generate-tests/generateScenarioRestTool.js";
27
+ import { registerDiscoverTestsTool } from "./tools/test-maintenance/discoverTestsTool.js";
28
+ import { registerAnalyzeTestDriftTool } from "./tools/test-maintenance/analyzeTestDriftTool.js";
29
+ import { registerExecuteBatchTestsTool } from "./tools/test-maintenance/executeBatchTestsTool.js";
30
+ import { registerCalculateHealthScoresTool } from "./tools/test-maintenance/calculateHealthScoresTool.js";
31
+ import { registerActionsTool } from "./tools/test-maintenance/actionsTool.js";
32
+ import { registerStateCleanupTool } from "./tools/test-maintenance/stateCleanupTool.js";
33
+ import { AnalyticsService } from "./services/AnalyticsService.js";
26
34
  const server = new McpServer({
27
35
  name: "Skyramp MCP Server",
28
36
  version: "1.0.0",
37
+ }, {
29
38
  capabilities: {
30
39
  tools: {
31
40
  listChanged: true,
@@ -40,6 +49,7 @@ logger.info("Starting prompt registration process");
40
49
  const prompts = [
41
50
  registerTestGenerationPrompt,
42
51
  registerStartTraceCollectionPrompt,
52
+ registerTestHealthPrompt,
43
53
  ];
44
54
  prompts.forEach((registerPrompt) => registerPrompt(server));
45
55
  logger.info("All prompts registered successfully");
@@ -62,17 +72,17 @@ const codeQualityTools = [
62
72
  registerCodeReuseTool,
63
73
  ];
64
74
  codeQualityTools.forEach((registerTool) => registerTool(server));
65
- // Register internal/test recommendation tools (controlled by environment variable)
66
- const enableInternalTools = process.env.SKYRAMP_ENABLE_INTERNAL_TOOLS === "true";
67
- if (enableInternalTools) {
68
- logger.info("Internal tools enabled - registering test recommendation tools");
69
- registerAnalyzeRepositoryTool(server);
70
- registerMapTestsTool(server);
71
- registerRecommendTestsTool(server);
72
- }
73
- else {
74
- logger.info("Internal tools disabled. Set SKYRAMP_ENABLE_INTERNAL_TOOLS=true to enable.");
75
- }
75
+ // Register test recommendation tools
76
+ registerAnalyzeRepositoryTool(server);
77
+ registerMapTestsTool(server);
78
+ registerRecommendTestsTool(server);
79
+ // Register test maintenance tools
80
+ registerDiscoverTestsTool(server);
81
+ registerAnalyzeTestDriftTool(server);
82
+ registerExecuteBatchTestsTool(server);
83
+ registerCalculateHealthScoresTool(server);
84
+ registerActionsTool(server);
85
+ registerStateCleanupTool(server);
76
86
  // Register other Skyramp tools
77
87
  const infrastructureTools = [
78
88
  registerLoginTool,
@@ -82,6 +92,35 @@ const infrastructureTools = [
82
92
  registerTraceStopTool,
83
93
  ];
84
94
  infrastructureTools.forEach((registerTool) => registerTool(server));
95
+ // Global error handlers for crash telemetry
96
+ process.on("uncaughtException", async (error) => {
97
+ logger.critical("Uncaught exception - MCP server crashing", {
98
+ error: error.message,
99
+ stack: error.stack,
100
+ });
101
+ try {
102
+ await AnalyticsService.pushServerCrashEvent("uncaughtException", error.message, error.stack);
103
+ }
104
+ catch (telemetryError) {
105
+ logger.error("Failed to send crash telemetry", { telemetryError });
106
+ }
107
+ process.exit(1);
108
+ });
109
+ process.on("unhandledRejection", async (reason, promise) => {
110
+ const errorMessage = reason instanceof Error ? reason.message : String(reason);
111
+ const errorStack = reason instanceof Error ? reason.stack : undefined;
112
+ logger.critical("Unhandled promise rejection - MCP server crashing", {
113
+ error: errorMessage,
114
+ stack: errorStack,
115
+ });
116
+ try {
117
+ await AnalyticsService.pushServerCrashEvent("unhandledRejection", errorMessage, errorStack);
118
+ }
119
+ catch (telemetryError) {
120
+ logger.error("Failed to send crash telemetry", { telemetryError });
121
+ }
122
+ process.exit(1);
123
+ });
85
124
  // Start MCP server
86
125
  async function main() {
87
126
  const transport = new StdioServerTransport();
@@ -102,10 +141,16 @@ async function main() {
102
141
  process.exit(0);
103
142
  });
104
143
  }
105
- main().catch((error) => {
144
+ main().catch(async (error) => {
106
145
  logger.critical("Fatal error in main()", {
107
146
  error: error.message,
108
147
  stack: error.stack,
109
148
  });
149
+ try {
150
+ await AnalyticsService.pushServerCrashEvent("mainFatalError", error.message, error.stack);
151
+ }
152
+ catch (telemetryError) {
153
+ logger.error("Failed to send crash telemetry", { telemetryError });
154
+ }
110
155
  process.exit(1);
111
156
  });
@@ -144,7 +144,7 @@ NOT A HELPER FUNCTION (do not extract):
144
144
  4. Found no other test files? → SKIP to Step 6, NO utils file
145
145
  5. Other test files identical to current? → SKIP to Step 6, NO utils file
146
146
 
147
- **MANDATORY**: After code reuse is complete, proceed to modularization by calling skyramp_modularization tool
147
+ **MANDATORY**: After code reuse is complete, proceed to modularization by calling skyramp_modularization tool ONLY for UI, E2E, INTEGRATION or LOAD test types generated from traces.
148
148
 
149
149
  SUMMARIZE THE CODE REUSE PROCESS AND THE RESULTS.
150
150
  `;
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Build optimized drift analysis prompt - compares TEST expectations vs CURRENT CODE
3
+ */
4
+ export function buildDriftAnalysisPrompt(testType, testFile, fileChanges, apiSchemaChanges, uiComponentChanges) {
5
+ const isUiTest = testType === "ui" || testType === "e2e";
6
+ return `# Test Drift Analysis
7
+
8
+ You are analyzing drift for a **${testType.toUpperCase()}** test to find breaking changes.
9
+
10
+ ## Test File
11
+ ${testFile}
12
+
13
+ ## Changed Files (${fileChanges.length})
14
+ ${fileChanges.length > 0
15
+ ? fileChanges
16
+ .map((c) => `- ${c.file} (+${c.linesAdded}/-${c.linesRemoved})`)
17
+ .join("\n")
18
+ : "No changes detected"}
19
+
20
+ ${buildSchemaChangesSection(apiSchemaChanges, uiComponentChanges)}
21
+
22
+ ## Your Task
23
+
24
+ ${isUiTest ? buildUiAnalysisInstructions() : buildApiAnalysisInstructions()}
25
+
26
+ ## Scoring Guide
27
+ - **85-100**: Critical breaking changes (type mismatch, missing endpoint/selector, auth change)
28
+ - **60-84**: High impact (path changed, required field added, status code change)
29
+ - **40-59**: Medium impact (optional field removed, component renamed)
30
+ - **20-39**: Low impact (new optional fields, minor refactoring)
31
+ - **0-19**: Minimal/no impact
32
+
33
+ ## Output Format (JSON)
34
+ \`\`\`json
35
+ {
36
+ "driftScore": 0-100,
37
+ "reasoning": "Concise explanation of key mismatches",
38
+ "breakingChanges": ["specific mismatch 1", "specific mismatch 2"],
39
+ "apiDependencyChanges": [{"file": "", "linesAdded": 0, "linesRemoved": 0, "updates": ""}],
40
+ "uiDependencyChanges": [{"file": "", "linesAdded": 0, "linesRemoved": 0, "updates": ""}],
41
+ "fieldTypeChanges": [{"file": "", "field": "", "testExpects": "", "codeHas": ""}],
42
+ "endpointChanges": [{"type": "removed|modified", "path": "", "method": "", "issue": ""}],
43
+ "selectorChanges": [{"file": "", "testExpects": "", "codeHas": ""}]
44
+ }
45
+ \`\`\``;
46
+ }
47
+ function buildSchemaChangesSection(apiSchemaChanges, uiComponentChanges) {
48
+ let section = "";
49
+ if (apiSchemaChanges) {
50
+ const removed = apiSchemaChanges.endpointsRemoved;
51
+ const modified = apiSchemaChanges.endpointsModified;
52
+ const authChanged = apiSchemaChanges.authenticationChanged;
53
+ if (removed.length > 0 || modified.length > 0 || authChanged) {
54
+ section += "\n## API Schema Changes\n";
55
+ if (removed.length > 0) {
56
+ section += `- **Removed**: ${removed
57
+ .map((e) => `${e.method} ${e.path}`)
58
+ .join(", ")}\n`;
59
+ }
60
+ if (modified.length > 0) {
61
+ section += `- **Modified**: ${modified
62
+ .map((e) => `${e.method} ${e.path}`)
63
+ .join(", ")}\n`;
64
+ }
65
+ if (authChanged) {
66
+ section += "- **Authentication**: Changed\n";
67
+ }
68
+ }
69
+ }
70
+ if (uiComponentChanges) {
71
+ const hasChanges = uiComponentChanges.componentFiles.length > 0 ||
72
+ uiComponentChanges.routeFiles.length > 0 ||
73
+ uiComponentChanges.hasSelectorsChanges ||
74
+ uiComponentChanges.hasStylingChanges;
75
+ if (hasChanges) {
76
+ section += "\n## UI Component Changes\n";
77
+ if (uiComponentChanges.componentFiles.length > 0) {
78
+ section += `- **Components**: ${uiComponentChanges.componentFiles.length} files\n`;
79
+ }
80
+ if (uiComponentChanges.routeFiles.length > 0) {
81
+ section += `- **Routes**: ${uiComponentChanges.routeFiles.length} files\n`;
82
+ }
83
+ if (uiComponentChanges.hasSelectorsChanges) {
84
+ section += "- **Selectors**: Likely changed\n";
85
+ }
86
+ if (uiComponentChanges.hasStylingChanges) {
87
+ section += "- **Styles**: Changed\n";
88
+ }
89
+ }
90
+ }
91
+ return section;
92
+ }
93
+ function buildUiAnalysisInstructions() {
94
+ return `### Analysis Steps
95
+
96
+ 1. **Extract Test Selectors**
97
+ - Find: \`getByTestId()\`, \`querySelector()\`, \`getElementById()\`, \`getByRole()\`
98
+ - Record: selector type and exact value
99
+
100
+ 2. **Analyze Git Diffs**
101
+ - Look for changes in: \`data-testid\`, \`className\`, \`id\`, \`role\`
102
+ - Check: Lines with "-" (removed) vs "+" (added)
103
+ - Pattern: \`- data-testid="old-value"\` → \`+ data-testid="new-value"\`
104
+
105
+ 3. **Compare & Detect Breaks**
106
+ - ❌ **CRITICAL**: Test selector not found in current code
107
+ - ❌ **CRITICAL**: Selector value changed (\`submit-button\` → \`submit-btn\`)
108
+ - ❌ **CRITICAL**: Element type changed (\`<button>\` → \`<div>\`)
109
+ - ❌ **HIGH**: Route changed (\`/products\` → \`/items\`)
110
+ - ❌ **HIGH**: Component renamed/removed
111
+
112
+ 4. **Categorize Changes**
113
+ - **uiDependencyChanges**: Components, pages, templates (.tsx, .jsx, .vue)
114
+ - **apiDependencyChanges**: API calls, services (if E2E test)
115
+
116
+ ### Key Patterns
117
+ - Selector mismatch → Score 85+
118
+ - Route change → Score 75+
119
+ - Styling only → Score 20-39`;
120
+ }
121
+ function buildApiAnalysisInstructions() {
122
+ return `### Analysis Steps
123
+
124
+ 1. **Extract Test Request Data**
125
+ - Find: Request bodies, payloads, field names
126
+ - Infer types from values:
127
+ * \`"text"\` = string
128
+ * \`123\` = integer
129
+ * \`12.5\` = decimal/float
130
+ * \`true\`/\`false\` = boolean
131
+ * \`[]\` = array
132
+ * \`null\` = optional
133
+
134
+ 2. **Analyze Git Diffs**
135
+ - Look for field type changes: \`- field: type_A\` → \`+ field: type_B\`
136
+ - Look for endpoint changes: \`- @app.post("/old")\` → \`+ @app.post("/new")\`
137
+ - Look for auth changes: \`- def endpoint()\` → \`+ @require_auth def endpoint()\`
138
+ - Check status codes: \`- return 200\` → \`+ return 201\`
139
+
140
+ 3. **Compare & Detect Breaks**
141
+ - ❌ **CRITICAL**: Field type changed (\`int\` → \`string\`)
142
+ - ❌ **CRITICAL**: Test value type ≠ code field type
143
+ - ❌ **CRITICAL**: Endpoint removed or path changed
144
+ - ❌ **HIGH**: Required field added (test doesn't send it)
145
+ - ❌ **HIGH**: Auth added/removed
146
+ - ❌ **HIGH**: Status code changed
147
+ - ⚠️ **MEDIUM**: Response field removed (test expects it)
148
+
149
+ 4. **Categorize Changes**
150
+ - **apiDependencyChanges**: Routes, controllers, models, schemas
151
+ - **uiDependencyChanges**: Only if E2E test with UI components
152
+
153
+ ### Key Patterns
154
+ - Type mismatch in diff → Score 85+
155
+ - Endpoint removed → Score 85+
156
+ - Auth change → Score 75+
157
+ - New required field → Score 75+
158
+ - Response structure change → Score 60-74`;
159
+ }
@@ -137,6 +137,8 @@ Use the EXACT same values from the original test when calling helpers.
137
137
  ## STEP 5: VERIFY - CRITICAL CHECKS
138
138
 
139
139
  **BUGS TO AVOID:**
140
+ - [ ] **NO TYPE ANNOTATIONS FOR \`page\` IN FUNCTION SIGNATURES** - Parameters must be untyped (e.g., \`page\`, not \`page: Page\`)
141
+ - [ ] **NO RETURN TYPES** - Do not add \`: Promise<void>\` or any return type annotations
140
142
  - [ ] **ALL field mappings verified** - EVERY \`.fill()\` uses the correct parameter. Check field gets quantity parameter, NOT hardcoded
141
143
  - [ ] **No duplicate helpers** - Consolidate similar helpers (e.g., one createOrder vs createSimpleOrder + createOrderWithItems)
142
144
  - [ ] **No nested helpers** - Helpers don't call other helpers unnecessarily
@@ -15,8 +15,8 @@ export function registerTestGenerationPrompt(mcpServer) {
15
15
  - Provide clear execution instructions for generated tests
16
16
  - Answer questions about Skyramp capabilities and testing concepts
17
17
  - ALWAYS SHOW STEPS TO GENERATE TEST USING MCP TOOLS AND NEVER SHOW THE CLI COMMANDS.
18
- - CRITICAL: UI, INTEGRATION, E2E TESTS MUST BE MODULARIZED USING skyramp_modularization TOOL. ALWAYS ADD A TASK TO MODULARIZE THE TEST USING skyramp_modularization TOOL AFTER GENERATING THESE(UI, INTEGRATION, E2E) TESTS.
19
- - **CRITICAL: skyramp_reuse_code TOOL MUST BE CALLED IF DURING THE TEST GENERATION THE CODE REUSE FLAG IS SET TO TRUE EXPLICITLY BY THE USER.**
18
+ - **CRITICAL: ONLY UI, INTEGRATION, E2E, LOAD TESTS GENERATED FROM TRACES MUST BE MODULARIZED USING skyramp_modularization TOOL. ADD A TASK TO MODULARIZE THE TEST USING skyramp_modularization TOOL AFTER GENERATING THESE(UI, INTEGRATION, E2E, LOAD) TESTS. DO NOT MODULARIZE THESE TESTS IF THEY ARE NOT GENERATED FROM TRACES.**
19
+ - **CRITICAL: skyramp_reuse_code TOOL MUST BE CALLED IF DURING THE TEST GENERATION THE CODE REUSE FLAG IS SET TO TRUE EXPLICITLY BY THE USER AND THE TEST IS GENERATED FROM TRACES.**
20
20
 
21
21
  **MANDATORY RULES**:
22
22
  1. **Priority Scores Must Remain Unchanged**: When a test type is missing required inputs (e.g., Playwright recordings, traces), **DO NOT**:
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Register the test health analysis prompt
3
+ *
4
+ * This prompt guides the AI to perform EXPLICIT TOOL CHAINING:
5
+ * 1. Call skyramp_discover_tests (If user provides a list of tests, skip this step)
6
+ * 2. Call skyramp_analyze_test_drift (with stateFile from discovery or list of tests)
7
+ * 3. (Optional) Call skyramp_execute_tests_batch (with stateFile)
8
+ * 4. Call skyramp_calculate_health_scores (with stateFile)
9
+ * 5. Call skyramp_actions (with stateFile) to perform test updates
10
+ */
11
+ export function registerTestHealthPrompt(server) {
12
+ server.prompt("skyramp_test_health_analysis", "Skyramp Test Health Analysis", {}, () => {
13
+ const messages = [
14
+ {
15
+ role: "user",
16
+ content: {
17
+ type: "text",
18
+ text: `Check health of test suite
19
+
20
+ Analyze the health of my test suite.
21
+
22
+ Repository: /path/to/repository`,
23
+ },
24
+ },
25
+ {
26
+ role: "assistant",
27
+ content: {
28
+ type: "text",
29
+ text: `I'll analyze your test suite health through explicit tool chaining with state files for maximum efficiency.
30
+
31
+ **CRITICAL: YOU MUST DISPLAY BELOW MESSAGE BEFORE ANY TOOL CALL:**
32
+ ** This tool is currently in Early Preview stage. Please verify the results. **
33
+
34
+ **The Analysis Process (State File Mode - 98% Token Reduction):**
35
+
36
+ 1. **Discovery (Optional)** - Call \`skyramp_discover_tests\` → Returns stateFile
37
+ 2. **Drift Analysis** - Call \`skyramp_analyze_test_drift\` with stateFile → Returns updated stateFile
38
+ 3. **Execution** - Call \`skyramp_execute_tests_batch\` with stateFile (Optional, only if user requests) → Returns updated stateFile
39
+ 4. **Health Scoring** - Call \`skyramp_calculate_health_scores\` with stateFile → Returns updated stateFile with health data
40
+ 5. **Perform Actions** - Call \`skyramp_actions\` with stateFile → Performs test updates
41
+
42
+ **State File Benefits:**
43
+ - Reduces token usage by 98%+ (from ~2.6MB to ~25KB for 100 tests)
44
+ - Faster processing
45
+ - Intermediate results saved to /tmp for debugging
46
+
47
+ Before I start, should I execute your tests (Step 3)?
48
+
49
+ **YES** (~5-10 min, requires Docker):
50
+ - Most comprehensive assessment
51
+ - Identifies actually failing tests
52
+
53
+ **NO** (~2-3 min):
54
+ - Faster, drift-based analysis only
55
+
56
+ Please respond "yes" or "no".`,
57
+ },
58
+ },
59
+ {
60
+ role: "user",
61
+ content: {
62
+ type: "text",
63
+ text: "no",
64
+ },
65
+ },
66
+ {
67
+ role: "assistant",
68
+ content: {
69
+ type: "text",
70
+ text: `Perfect! I'll run the analysis without execution. Starting now:
71
+
72
+ **Step 1: Discovering tests...**
73
+
74
+ I'll call \`skyramp_discover_tests\` to find all Skyramp tests in your repository.`,
75
+ },
76
+ },
77
+ ];
78
+ return {
79
+ messages,
80
+ };
81
+ });
82
+ }
@@ -0,0 +1,86 @@
1
+ import { pushToolEvent } from "@skyramp/skyramp";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { fileURLToPath } from "url";
5
+ export class AnalyticsService {
6
+ static entryPoint = "mcp";
7
+ static async pushTestGenerationToolEvent(toolName, result, params) {
8
+ const analyticsResult = {};
9
+ analyticsResult["prompt"] = params.prompt;
10
+ analyticsResult["language"] = params.language;
11
+ this.pushMCPToolEvent(toolName, result, analyticsResult);
12
+ }
13
+ static async pushMCPToolEvent(toolName, result, params) {
14
+ // Error message.
15
+ let errorMessage = "";
16
+ if (result && result.isError) {
17
+ for (const content of result?.content ?? []) {
18
+ if ("text" in content && content.text) {
19
+ errorMessage += content.text + ", ";
20
+ }
21
+ }
22
+ if (errorMessage.length > 0) {
23
+ errorMessage = errorMessage.slice(0, -2);
24
+ }
25
+ }
26
+ //add mcp server version from package.json
27
+ params.mcpServerVersion = getMCPPackageVersion();
28
+ console.error("pushToolEvent", {
29
+ entryPoint: this.entryPoint,
30
+ toolName: toolName,
31
+ errorMessage: errorMessage,
32
+ analyticsResult: params,
33
+ });
34
+ // console.error(
35
+ // "tool name ::: ",
36
+ // toolName,
37
+ // "params ::: ",
38
+ // params,
39
+ // "error message ::: ",
40
+ // errorMessage,
41
+ // );
42
+ await pushToolEvent(this.entryPoint, toolName, errorMessage, params);
43
+ }
44
+ /**
45
+ * Track server crash events
46
+ */
47
+ static async pushServerCrashEvent(crashType, errorMessage, errorStack) {
48
+ const params = {
49
+ crashType: crashType,
50
+ errorStack: errorStack || "no stack trace",
51
+ mcpServerVersion: getMCPPackageVersion(),
52
+ };
53
+ console.error("pushServerCrashEvent", {
54
+ entryPoint: this.entryPoint,
55
+ toolName: "mcp_server_crash",
56
+ errorMessage: errorMessage,
57
+ analyticsResult: params,
58
+ });
59
+ await pushToolEvent(this.entryPoint, "mcp_server_crash", errorMessage, params);
60
+ }
61
+ /**
62
+ * Track tool timeout events
63
+ */
64
+ static async pushToolTimeoutEvent(toolName, timeoutMs, params) {
65
+ const errorMessage = `Tool ${toolName} timed out after ${timeoutMs}ms`;
66
+ const timeoutParams = {
67
+ ...params,
68
+ timeoutMs: timeoutMs.toString(),
69
+ mcpServerVersion: getMCPPackageVersion(),
70
+ };
71
+ console.error("pushToolTimeoutEvent", {
72
+ entryPoint: this.entryPoint,
73
+ toolName: toolName,
74
+ errorMessage: errorMessage,
75
+ analyticsResult: timeoutParams,
76
+ });
77
+ await pushToolEvent(this.entryPoint, `${toolName}_timeout`, errorMessage, timeoutParams);
78
+ }
79
+ }
80
+ function getMCPPackageVersion() {
81
+ const __filename = fileURLToPath(import.meta.url);
82
+ const __dirname = path.dirname(__filename);
83
+ const packageJson = fs.readFileSync(path.join(__dirname, "../../package.json"), "utf8");
84
+ const packageJsonData = JSON.parse(packageJson);
85
+ return packageJsonData.version;
86
+ }