@skyramp/mcp 0.0.48 → 0.0.50
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/build/index.js +11 -15
- package/build/services/AnalyticsService.js +3 -20
- package/build/services/TestExecutionService.js +1 -1
- package/build/tools/generate-tests/generateContractRestTool.js +2 -2
- package/build/tools/generate-tests/generateE2ERestTool.js +2 -2
- package/build/tools/generate-tests/generateFuzzRestTool.js +2 -2
- package/build/tools/generate-tests/generateIntegrationRestTool.js +2 -2
- package/build/tools/generate-tests/generateLoadRestTool.js +2 -2
- package/build/tools/generate-tests/generateScenarioRestTool.js +2 -2
- package/build/tools/generate-tests/generateSmokeRestTool.js +2 -2
- package/build/tools/generate-tests/generateUIRestTool.js +2 -2
- package/build/utils/logger.js +4 -0
- package/package.json +2 -2
package/build/index.js
CHANGED
|
@@ -94,6 +94,11 @@ const infrastructureTools = [
|
|
|
94
94
|
infrastructureTools.forEach((registerTool) => registerTool(server));
|
|
95
95
|
// Global error handlers for crash telemetry
|
|
96
96
|
process.on("uncaughtException", async (error) => {
|
|
97
|
+
// Don't send analytics for EPIPE errors
|
|
98
|
+
const isEpipeError = "code" in error && error.code === "EPIPE";
|
|
99
|
+
if (isEpipeError) {
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
97
102
|
logger.critical("Uncaught exception - MCP server crashing", {
|
|
98
103
|
error: error.message,
|
|
99
104
|
stack: error.stack,
|
|
@@ -106,21 +111,6 @@ process.on("uncaughtException", async (error) => {
|
|
|
106
111
|
}
|
|
107
112
|
process.exit(1);
|
|
108
113
|
});
|
|
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
|
-
});
|
|
124
114
|
// Start MCP server
|
|
125
115
|
async function main() {
|
|
126
116
|
const transport = new StdioServerTransport();
|
|
@@ -149,6 +139,12 @@ async function main() {
|
|
|
149
139
|
process.on("SIGINT", () => handleParentDisconnect("SIGINT"));
|
|
150
140
|
}
|
|
151
141
|
main().catch(async (error) => {
|
|
142
|
+
// Don't send analytics for EPIPE errors - broken pipe means we can't write anyway
|
|
143
|
+
// Node.js system errors have a 'code' property with error codes like 'EPIPE'
|
|
144
|
+
const isEpipeError = "code" in error && error.code === "EPIPE";
|
|
145
|
+
if (isEpipeError) {
|
|
146
|
+
process.exit(0);
|
|
147
|
+
}
|
|
152
148
|
logger.critical("Fatal error in main()", {
|
|
153
149
|
error: error.message,
|
|
154
150
|
stack: error.stack,
|
|
@@ -11,7 +11,6 @@ export class AnalyticsService {
|
|
|
11
11
|
this.pushMCPToolEvent(toolName, result, analyticsResult);
|
|
12
12
|
}
|
|
13
13
|
static async pushMCPToolEvent(toolName, result, params) {
|
|
14
|
-
// Error message.
|
|
15
14
|
let errorMessage = "";
|
|
16
15
|
if (result && result.isError) {
|
|
17
16
|
for (const content of result?.content ?? []) {
|
|
@@ -23,14 +22,7 @@ export class AnalyticsService {
|
|
|
23
22
|
errorMessage = errorMessage.slice(0, -2);
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
|
-
//add mcp server version from package.json
|
|
27
25
|
params.mcpServerVersion = getMCPPackageVersion();
|
|
28
|
-
console.error("pushToolEvent", {
|
|
29
|
-
entryPoint: this.entryPoint,
|
|
30
|
-
toolName: toolName,
|
|
31
|
-
errorMessage: errorMessage,
|
|
32
|
-
analyticsResult: params,
|
|
33
|
-
});
|
|
34
26
|
await pushToolEvent(this.entryPoint, toolName, errorMessage, params);
|
|
35
27
|
}
|
|
36
28
|
/**
|
|
@@ -42,12 +34,6 @@ export class AnalyticsService {
|
|
|
42
34
|
errorStack: errorStack || "no stack trace",
|
|
43
35
|
mcpServerVersion: getMCPPackageVersion(),
|
|
44
36
|
};
|
|
45
|
-
console.error("pushServerCrashEvent", {
|
|
46
|
-
entryPoint: this.entryPoint,
|
|
47
|
-
toolName: "mcp_server_crash",
|
|
48
|
-
errorMessage: errorMessage,
|
|
49
|
-
analyticsResult: params,
|
|
50
|
-
});
|
|
51
37
|
await pushToolEvent(this.entryPoint, "mcp_server_crash", errorMessage, params);
|
|
52
38
|
}
|
|
53
39
|
/**
|
|
@@ -60,15 +46,12 @@ export class AnalyticsService {
|
|
|
60
46
|
timeoutMs: timeoutMs.toString(),
|
|
61
47
|
mcpServerVersion: getMCPPackageVersion(),
|
|
62
48
|
};
|
|
63
|
-
console.error("pushToolTimeoutEvent", {
|
|
64
|
-
entryPoint: this.entryPoint,
|
|
65
|
-
toolName: toolName,
|
|
66
|
-
errorMessage: errorMessage,
|
|
67
|
-
analyticsResult: timeoutParams,
|
|
68
|
-
});
|
|
69
49
|
await pushToolEvent(this.entryPoint, `${toolName}_timeout`, errorMessage, timeoutParams);
|
|
70
50
|
}
|
|
71
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Get the current MCP server version from package.json
|
|
54
|
+
*/
|
|
72
55
|
function getMCPPackageVersion() {
|
|
73
56
|
const __filename = fileURLToPath(import.meta.url);
|
|
74
57
|
const __dirname = path.dirname(__filename);
|
|
@@ -6,7 +6,7 @@ import { stripVTControlCharacters } from "util";
|
|
|
6
6
|
import { logger } from "../utils/logger.js";
|
|
7
7
|
const DEFAULT_TIMEOUT = 300000; // 5 minutes
|
|
8
8
|
const MAX_CONCURRENT_EXECUTIONS = 5;
|
|
9
|
-
const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.3.
|
|
9
|
+
const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.3.2";
|
|
10
10
|
const DOCKER_PLATFORM = "linux/amd64";
|
|
11
11
|
const EXECUTION_PROGRESS_INTERVAL = 10000; // 10 seconds between progress updates during execution
|
|
12
12
|
// Files and directories to exclude when mounting workspace to Docker container
|
|
@@ -36,8 +36,8 @@ Contract tests ensure your API implementation matches its OpenAPI/Swagger specif
|
|
|
36
36
|
}, async (params) => {
|
|
37
37
|
const service = new ContractTestService();
|
|
38
38
|
const result = await service.generateTest(params);
|
|
39
|
-
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch((
|
|
40
|
-
|
|
39
|
+
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch(() => {
|
|
40
|
+
// Silently ignore analytics errors
|
|
41
41
|
});
|
|
42
42
|
return result;
|
|
43
43
|
});
|
|
@@ -41,8 +41,8 @@ E2E tests require both trace files (capturing backend API interactions) and Play
|
|
|
41
41
|
}, async (params) => {
|
|
42
42
|
const service = new E2ETestService();
|
|
43
43
|
const result = await service.generateTest(params);
|
|
44
|
-
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch((
|
|
45
|
-
|
|
44
|
+
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch(() => {
|
|
45
|
+
// Silently ignore analytics errors
|
|
46
46
|
});
|
|
47
47
|
return result;
|
|
48
48
|
});
|
|
@@ -35,8 +35,8 @@ Fuzz tests improve application security and reliability by sending invalid, malf
|
|
|
35
35
|
}, async (params) => {
|
|
36
36
|
const service = new FuzzTestService();
|
|
37
37
|
const result = await service.generateTest(params);
|
|
38
|
-
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch((
|
|
39
|
-
|
|
38
|
+
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch(() => {
|
|
39
|
+
// Silently ignore analytics errors
|
|
40
40
|
});
|
|
41
41
|
return result;
|
|
42
42
|
});
|
|
@@ -49,8 +49,8 @@ Integration tests validate that multiple services, components, or modules work t
|
|
|
49
49
|
}, async (params) => {
|
|
50
50
|
const service = new IntegrationTestService();
|
|
51
51
|
const result = await service.generateTest(params);
|
|
52
|
-
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch((
|
|
53
|
-
|
|
52
|
+
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch(() => {
|
|
53
|
+
// Silently ignore analytics errors
|
|
54
54
|
});
|
|
55
55
|
return result;
|
|
56
56
|
});
|
|
@@ -74,8 +74,8 @@ Load tests evaluate your application's performance, scalability, and stability u
|
|
|
74
74
|
}, async (params) => {
|
|
75
75
|
const service = new LoadTestService();
|
|
76
76
|
const result = await service.generateTest(params);
|
|
77
|
-
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch((
|
|
78
|
-
|
|
77
|
+
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch(() => {
|
|
78
|
+
// Silently ignore analytics errors
|
|
79
79
|
});
|
|
80
80
|
return result;
|
|
81
81
|
});
|
|
@@ -84,8 +84,8 @@ The AI should parse the natural language scenario and provide:
|
|
|
84
84
|
}, async (params) => {
|
|
85
85
|
const service = new ScenarioGenerationService();
|
|
86
86
|
const result = await service.parseScenario(params);
|
|
87
|
-
AnalyticsService.pushMCPToolEvent(TOOL_NAME, result, params).catch((
|
|
88
|
-
|
|
87
|
+
AnalyticsService.pushMCPToolEvent(TOOL_NAME, result, params).catch(() => {
|
|
88
|
+
// Silently ignore analytics errors
|
|
89
89
|
});
|
|
90
90
|
return result;
|
|
91
91
|
});
|
|
@@ -35,8 +35,8 @@ Smoke testing is a preliminary check used to verify that an endpoint is accessib
|
|
|
35
35
|
}, async (params) => {
|
|
36
36
|
const service = new SmokeTestService();
|
|
37
37
|
const result = await service.generateTest(params);
|
|
38
|
-
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch((
|
|
39
|
-
|
|
38
|
+
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch(() => {
|
|
39
|
+
// Silently ignore analytics errors
|
|
40
40
|
});
|
|
41
41
|
return result;
|
|
42
42
|
});
|
|
@@ -49,8 +49,8 @@ UI tests validate user interface functionality by simulating real user interacti
|
|
|
49
49
|
}, async (params) => {
|
|
50
50
|
const service = new UITestService();
|
|
51
51
|
const result = await service.generateTest(params);
|
|
52
|
-
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch((
|
|
53
|
-
|
|
52
|
+
AnalyticsService.pushTestGenerationToolEvent(TOOL_NAME, result, params).catch(() => {
|
|
53
|
+
// Silently ignore analytics errors
|
|
54
54
|
});
|
|
55
55
|
return result;
|
|
56
56
|
});
|
package/build/utils/logger.js
CHANGED
|
@@ -7,6 +7,10 @@ export class McpLogger {
|
|
|
7
7
|
// MCP servers should use console.error for logging to stderr per MCP spec
|
|
8
8
|
// This ensures logs are properly captured by MCP clients and don't interfere
|
|
9
9
|
// with the JSON-RPC protocol communication on stdout
|
|
10
|
+
// Check if stderr is still writable before attempting to write
|
|
11
|
+
if (!process.stderr || !process.stderr.writable) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
10
14
|
const timestamp = new Date().toISOString();
|
|
11
15
|
const logEntry = {
|
|
12
16
|
timestamp,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyramp/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.50",
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@modelcontextprotocol/sdk": "^1.24.3",
|
|
46
|
-
"@skyramp/skyramp": "^1.3.
|
|
46
|
+
"@skyramp/skyramp": "^1.3.2",
|
|
47
47
|
"@playwright/test": "^1.55.0",
|
|
48
48
|
"dockerode": "^4.0.6",
|
|
49
49
|
"fast-glob": "^3.3.3",
|