@skyramp/mcp 0.0.39 → 0.0.40
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.
|
@@ -16,6 +16,22 @@ export function registerStartTraceCollectionPrompt(mcpServer) {
|
|
|
16
16
|
* Once you have all the data call the start trace generation tool to start the trace collection.
|
|
17
17
|
* Always ask user if they want to enable playwright for trace collection.
|
|
18
18
|
|
|
19
|
+
**Playwright Configuration Options:**
|
|
20
|
+
When playwright is enabled for trace collection, you can optionally configure:
|
|
21
|
+
|
|
22
|
+
1. **Playwright Storage Path** (playwrightStoragePath):
|
|
23
|
+
- Path to a playwright session storage file containing authentication data (cookies, localStorage, sessionStorage, etc.)
|
|
24
|
+
- MUST be an absolute path like /path/to/storage.json
|
|
25
|
+
- Use this when you have manually created a session from the login flow and want to reuse it for future trace collections to avoid manual login every time
|
|
26
|
+
- The session file should be created beforehand using Playwright's storageState feature during the login flow
|
|
27
|
+
|
|
28
|
+
2. **Playwright Viewport Size** (playwrightViewportSize):
|
|
29
|
+
- Defines the browser window size for trace collection
|
|
30
|
+
- Supported formats:
|
|
31
|
+
* 'hd' - 1280x720
|
|
32
|
+
* 'full-hd' - 1920x1080
|
|
33
|
+
* '2k' - 2560x1440
|
|
34
|
+
* Custom: 'width,height' (e.g., '1920,1080')
|
|
19
35
|
|
|
20
36
|
**Example usage prompt for trace collection:**
|
|
21
37
|
* To start a trace collection session using agent, run the following command:
|
|
@@ -25,6 +41,9 @@ export function registerStartTraceCollectionPrompt(mcpServer) {
|
|
|
25
41
|
* To start a playwright trace collection session using agent, run the following command:
|
|
26
42
|
Start playwright trace collection with default settings.
|
|
27
43
|
|
|
44
|
+
**Example usage prompt for trace collection with playwright storage and viewport:**
|
|
45
|
+
* Start playwright trace collection with storage path /Users/dev/session-storage.json and viewport size full-hd
|
|
46
|
+
|
|
28
47
|
**CRITICAL: NEVER SHOW THE CLI COMMANDS.**
|
|
29
48
|
`,
|
|
30
49
|
},
|
|
@@ -5,7 +5,7 @@ import { Writable } from "stream";
|
|
|
5
5
|
import { logger } from "../utils/logger.js";
|
|
6
6
|
import { stripVTControlCharacters } from "util";
|
|
7
7
|
import fs from "fs";
|
|
8
|
-
const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.2.
|
|
8
|
+
const EXECUTOR_DOCKER_IMAGE = "skyramp/executor:v1.2.30";
|
|
9
9
|
const DOCKER_PLATFORM = "linux/amd64";
|
|
10
10
|
export function registerExecuteSkyrampTestTool(server) {
|
|
11
11
|
server.registerTool("skyramp_execute_test", {
|
|
@@ -3,6 +3,7 @@ import { SkyrampClient } from "@skyramp/skyramp";
|
|
|
3
3
|
import openProxyTerminalTracked from "../../utils/proxy-terminal.js";
|
|
4
4
|
import { TELEMETRY_entrypoint_FIELD_NAME } from "../../utils/utils.js";
|
|
5
5
|
import { logger } from "../../utils/logger.js";
|
|
6
|
+
import { basePlaywrightSchema } from "../../types/TestTypes.js";
|
|
6
7
|
export function registerTraceTool(server) {
|
|
7
8
|
server.registerTool("skyramp_start_trace_collection", {
|
|
8
9
|
description: `Start trace collection using Skyramp's comprehensive tracing capabilities.
|
|
@@ -26,6 +27,8 @@ For detailed documentation visit: https://www.skyramp.dev/docs/load-test/advance
|
|
|
26
27
|
.boolean()
|
|
27
28
|
.describe("Whether to enable Playwright for trace collection. Set to true for UI interactions, false for API-only tracing")
|
|
28
29
|
.default(true),
|
|
30
|
+
playwrightStoragePath: basePlaywrightSchema.shape.playwrightStoragePath,
|
|
31
|
+
playwrightViewportSize: basePlaywrightSchema.shape.playwrightViewportSize,
|
|
29
32
|
runtime: z
|
|
30
33
|
.string()
|
|
31
34
|
.default("docker")
|
|
@@ -79,6 +82,8 @@ For detailed documentation visit: https://www.skyramp.dev/docs/load-test/advance
|
|
|
79
82
|
generateNoProxy: params.noProxy,
|
|
80
83
|
unblock: true,
|
|
81
84
|
playwright: params.playwright,
|
|
85
|
+
playwrightStoragePath: params.playwrightStoragePath,
|
|
86
|
+
playwrightViewportSize: params.playwrightViewportSize,
|
|
82
87
|
entrypoint: TELEMETRY_entrypoint_FIELD_NAME,
|
|
83
88
|
};
|
|
84
89
|
try {
|
package/build/types/TestTypes.js
CHANGED
|
@@ -65,6 +65,19 @@ export const baseSchema = z.object({
|
|
|
65
65
|
.describe("Whether to overwrite existing files in the output directory"),
|
|
66
66
|
prompt: z.string().describe("The prompt user provided to generate the test"),
|
|
67
67
|
});
|
|
68
|
+
export const basePlaywrightSchema = z.object({
|
|
69
|
+
playwrightInput: z
|
|
70
|
+
.string()
|
|
71
|
+
.describe("MUST be absolute path to the playwright input file like /path/to/playwright-ui-test.zip and MUST be a zip file captured using start_trace_collection tool"),
|
|
72
|
+
playwrightStoragePath: z
|
|
73
|
+
.string()
|
|
74
|
+
.optional()
|
|
75
|
+
.describe("Path to playwright session storage for trace collection with playwright enabled. THE PATH MUST BE AN ABSOLUTE PATH LIKE /Users/<path>/<filename>.json"),
|
|
76
|
+
playwrightViewportSize: z
|
|
77
|
+
.string()
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("Viewport size for playwright browser. THE VALUE MUST BE IN THE FORMAT [hd, full-hd, 2k, 'x,y' e.g. '1920,1080' for 1920x1080 resolution]"),
|
|
80
|
+
});
|
|
68
81
|
export const baseTraceSchema = z.object({
|
|
69
82
|
trace: z
|
|
70
83
|
.string()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyramp/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.40",
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.11.4",
|
|
45
|
-
"@skyramp/skyramp": "^1.2.
|
|
45
|
+
"@skyramp/skyramp": "^1.2.30",
|
|
46
46
|
"@playwright/test": "^1.55.0",
|
|
47
47
|
"dockerode": "^4.0.6",
|
|
48
48
|
"zod": "^3.25.3"
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, jest } from "@jest/globals";
|
|
2
|
-
import { registerCodeReuseTool } from "../code-refactor/codeReuseTool.js";
|
|
3
|
-
import { getCodeReusePrompt } from "../../prompts/code-reuse.js";
|
|
4
|
-
// Mock the logger
|
|
5
|
-
jest.mock("../../utils/logger", () => ({
|
|
6
|
-
logger: {
|
|
7
|
-
info: jest.fn(),
|
|
8
|
-
error: jest.fn(),
|
|
9
|
-
warn: jest.fn(),
|
|
10
|
-
debug: jest.fn(),
|
|
11
|
-
},
|
|
12
|
-
}));
|
|
13
|
-
// Mock the prompt function
|
|
14
|
-
jest.mock("../../prompts/code-reuse", () => ({
|
|
15
|
-
getCodeReusePrompt: jest.fn(),
|
|
16
|
-
}));
|
|
17
|
-
describe("Code Reuse Tool", () => {
|
|
18
|
-
let mockServer;
|
|
19
|
-
let mockRegisterTool;
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
jest.clearAllMocks();
|
|
22
|
-
mockRegisterTool = jest.fn();
|
|
23
|
-
mockServer = {
|
|
24
|
-
registerTool: mockRegisterTool,
|
|
25
|
-
};
|
|
26
|
-
getCodeReusePrompt.mockReturnValue("Mocked prompt content");
|
|
27
|
-
});
|
|
28
|
-
describe("Tool Registration", () => {
|
|
29
|
-
it("should register the code reuse tool with correct configuration", () => {
|
|
30
|
-
registerCodeReuseTool(mockServer);
|
|
31
|
-
expect(mockRegisterTool).toHaveBeenCalledWith("skyramp_code_reuse", expect.objectContaining({
|
|
32
|
-
description: expect.stringContaining("Analyzes code for reuse opportunities"),
|
|
33
|
-
inputSchema: expect.objectContaining({
|
|
34
|
-
testFile: expect.any(Object),
|
|
35
|
-
language: expect.any(Object),
|
|
36
|
-
framework: expect.any(Object),
|
|
37
|
-
modularizeCode: expect.any(Object),
|
|
38
|
-
prompt: expect.any(Object),
|
|
39
|
-
}),
|
|
40
|
-
annotations: expect.objectContaining({
|
|
41
|
-
keywords: expect.arrayContaining([
|
|
42
|
-
"code reuse",
|
|
43
|
-
"duplicate code",
|
|
44
|
-
"helper functions",
|
|
45
|
-
"import",
|
|
46
|
-
"refactor",
|
|
47
|
-
]),
|
|
48
|
-
}),
|
|
49
|
-
}), expect.any(Function));
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
describe("Tool Execution", () => {
|
|
53
|
-
let toolHandler;
|
|
54
|
-
beforeEach(() => {
|
|
55
|
-
registerCodeReuseTool(mockServer);
|
|
56
|
-
toolHandler = mockRegisterTool.mock.calls[0][2];
|
|
57
|
-
});
|
|
58
|
-
it("should handle valid input parameters correctly", async () => {
|
|
59
|
-
const params = {
|
|
60
|
-
testFile: "/path/to/test.spec.ts",
|
|
61
|
-
language: "typescript",
|
|
62
|
-
framework: "playwright",
|
|
63
|
-
modularizeCode: true,
|
|
64
|
-
prompt: "Test code content to analyze",
|
|
65
|
-
};
|
|
66
|
-
const result = await toolHandler(params);
|
|
67
|
-
expect(getCodeReusePrompt).toHaveBeenCalledWith(params.testFile, params.language, params.modularizeCode);
|
|
68
|
-
expect(result).toEqual({
|
|
69
|
-
content: [
|
|
70
|
-
{
|
|
71
|
-
type: "text",
|
|
72
|
-
text: expect.stringContaining("Mocked prompt content"),
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
});
|
|
76
|
-
expect(result.content[0].text).toContain(params.testFile);
|
|
77
|
-
expect(result.content[0].text).toContain(params.language);
|
|
78
|
-
expect(result.content[0].text).toContain(params.framework);
|
|
79
|
-
expect(result.content[0].text).toContain(params.prompt);
|
|
80
|
-
});
|
|
81
|
-
it("should include analysis instructions in the output", async () => {
|
|
82
|
-
const params = {
|
|
83
|
-
testFile: "/path/to/test.spec.ts",
|
|
84
|
-
language: "typescript",
|
|
85
|
-
framework: "playwright",
|
|
86
|
-
modularizeCode: false,
|
|
87
|
-
prompt: "Code to analyze",
|
|
88
|
-
};
|
|
89
|
-
const result = await toolHandler(params);
|
|
90
|
-
const outputText = result.content[0].text;
|
|
91
|
-
expect(outputText).toContain("ANALYSIS INSTRUCTIONS");
|
|
92
|
-
expect(outputText).toContain("search for existing test files using glob_file_search");
|
|
93
|
-
expect(outputText).toContain("Read existing test files to identify reusable helper functions");
|
|
94
|
-
expect(outputText).toContain("Import and reuse existing functions instead of duplicating code");
|
|
95
|
-
expect(outputText).toContain("DO NOT modify existing utility files");
|
|
96
|
-
});
|
|
97
|
-
it("should handle different frameworks", async () => {
|
|
98
|
-
const frameworks = ["playwright", "jest", "pytest", "junit"];
|
|
99
|
-
for (const framework of frameworks) {
|
|
100
|
-
const params = {
|
|
101
|
-
testFile: "/path/to/test.spec.ts",
|
|
102
|
-
language: "typescript",
|
|
103
|
-
framework,
|
|
104
|
-
modularizeCode: false,
|
|
105
|
-
prompt: "Test framework code",
|
|
106
|
-
};
|
|
107
|
-
const result = await toolHandler(params);
|
|
108
|
-
expect(result.content[0].text).toContain(`Framework: ${framework}`);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
it("should handle modularizeCode flag variations", async () => {
|
|
112
|
-
const modularizeOptions = [true, false];
|
|
113
|
-
for (const modularizeCode of modularizeOptions) {
|
|
114
|
-
const params = {
|
|
115
|
-
testFile: "/path/to/test.spec.ts",
|
|
116
|
-
language: "typescript",
|
|
117
|
-
framework: "playwright",
|
|
118
|
-
modularizeCode,
|
|
119
|
-
prompt: "Code content",
|
|
120
|
-
};
|
|
121
|
-
await toolHandler(params);
|
|
122
|
-
expect(getCodeReusePrompt).toHaveBeenCalledWith(params.testFile, params.language, modularizeCode);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
it("should format output with proper sections", async () => {
|
|
126
|
-
const params = {
|
|
127
|
-
testFile: "/path/to/integration.test.ts",
|
|
128
|
-
language: "typescript",
|
|
129
|
-
framework: "playwright",
|
|
130
|
-
modularizeCode: true,
|
|
131
|
-
prompt: "Integration test code with potential reuse opportunities",
|
|
132
|
-
};
|
|
133
|
-
const result = await toolHandler(params);
|
|
134
|
-
const outputText = result.content[0].text;
|
|
135
|
-
// Check for proper section formatting
|
|
136
|
-
expect(outputText).toContain("CODE TO ANALYZE");
|
|
137
|
-
expect(outputText).toContain("ANALYSIS INSTRUCTIONS");
|
|
138
|
-
expect(outputText).toMatch(/═+/); // Section separators
|
|
139
|
-
expect(outputText).toContain("📝"); // Emoji indicators
|
|
140
|
-
expect(outputText).toContain("🎯"); // Analysis emoji
|
|
141
|
-
});
|
|
142
|
-
it("should include specific reuse categories in instructions", async () => {
|
|
143
|
-
const params = {
|
|
144
|
-
testFile: "/path/to/ui.test.ts",
|
|
145
|
-
language: "typescript",
|
|
146
|
-
framework: "playwright",
|
|
147
|
-
modularizeCode: true,
|
|
148
|
-
prompt: "UI test with authentication and form filling",
|
|
149
|
-
};
|
|
150
|
-
const result = await toolHandler(params);
|
|
151
|
-
const outputText = result.content[0].text;
|
|
152
|
-
expect(outputText).toContain("authentication");
|
|
153
|
-
expect(outputText).toContain("HTTP request");
|
|
154
|
-
expect(outputText).toContain("data setup");
|
|
155
|
-
expect(outputText).toContain("validation");
|
|
156
|
-
expect(outputText).toContain("utility functions");
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
describe("Input Validation", () => {
|
|
160
|
-
let toolHandler;
|
|
161
|
-
beforeEach(() => {
|
|
162
|
-
registerCodeReuseTool(mockServer);
|
|
163
|
-
toolHandler = mockRegisterTool.mock.calls[0][2];
|
|
164
|
-
});
|
|
165
|
-
it("should handle minimal required parameters", async () => {
|
|
166
|
-
const minimalParams = {
|
|
167
|
-
testFile: "/path/to/test.ts",
|
|
168
|
-
language: "typescript",
|
|
169
|
-
framework: "jest",
|
|
170
|
-
modularizeCode: false,
|
|
171
|
-
prompt: "Basic test code",
|
|
172
|
-
};
|
|
173
|
-
const result = await toolHandler(minimalParams);
|
|
174
|
-
expect(result.content).toHaveLength(1);
|
|
175
|
-
expect(result.content[0].type).toBe("text");
|
|
176
|
-
});
|
|
177
|
-
it("should preserve all input parameters in output", async () => {
|
|
178
|
-
const params = {
|
|
179
|
-
testFile: "/complex/path/to/advanced.test.ts",
|
|
180
|
-
language: "typescript",
|
|
181
|
-
framework: "playwright",
|
|
182
|
-
modularizeCode: true,
|
|
183
|
-
prompt: "Complex test code with multiple helper functions and duplicate patterns",
|
|
184
|
-
};
|
|
185
|
-
const result = await toolHandler(params);
|
|
186
|
-
const outputText = result.content[0].text;
|
|
187
|
-
expect(outputText).toContain(params.testFile);
|
|
188
|
-
expect(outputText).toContain(params.language);
|
|
189
|
-
expect(outputText).toContain(params.framework);
|
|
190
|
-
expect(outputText).toContain(params.prompt);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
describe("Error Handling", () => {
|
|
194
|
-
let toolHandler;
|
|
195
|
-
beforeEach(() => {
|
|
196
|
-
registerCodeReuseTool(mockServer);
|
|
197
|
-
toolHandler = mockRegisterTool.mock.calls[0][2];
|
|
198
|
-
});
|
|
199
|
-
it("should handle errors in prompt generation gracefully", async () => {
|
|
200
|
-
getCodeReusePrompt.mockImplementation(() => {
|
|
201
|
-
throw new Error("Prompt generation failed");
|
|
202
|
-
});
|
|
203
|
-
const params = {
|
|
204
|
-
testFile: "/path/to/test.ts",
|
|
205
|
-
language: "typescript",
|
|
206
|
-
framework: "jest",
|
|
207
|
-
modularizeCode: false,
|
|
208
|
-
prompt: "Test code",
|
|
209
|
-
};
|
|
210
|
-
await expect(toolHandler(params)).rejects.toThrow("Prompt generation failed");
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
describe("Integration with Prompt System", () => {
|
|
214
|
-
let toolHandler;
|
|
215
|
-
beforeEach(() => {
|
|
216
|
-
registerCodeReuseTool(mockServer);
|
|
217
|
-
toolHandler = mockRegisterTool.mock.calls[0][2];
|
|
218
|
-
});
|
|
219
|
-
it("should call getCodeReusePrompt with correct parameters", async () => {
|
|
220
|
-
const params = {
|
|
221
|
-
testFile: "/src/tests/api.test.ts",
|
|
222
|
-
language: "typescript",
|
|
223
|
-
framework: "jest",
|
|
224
|
-
modularizeCode: true,
|
|
225
|
-
prompt: "API test code",
|
|
226
|
-
};
|
|
227
|
-
await toolHandler(params);
|
|
228
|
-
expect(getCodeReusePrompt).toHaveBeenCalledTimes(1);
|
|
229
|
-
expect(getCodeReusePrompt).toHaveBeenCalledWith(params.testFile, params.language, params.modularizeCode);
|
|
230
|
-
});
|
|
231
|
-
it("should integrate prompt content with tool-specific instructions", async () => {
|
|
232
|
-
const mockPromptContent = "Custom analysis instructions from prompt";
|
|
233
|
-
getCodeReusePrompt.mockReturnValue(mockPromptContent);
|
|
234
|
-
const params = {
|
|
235
|
-
testFile: "/test/example.ts",
|
|
236
|
-
language: "typescript",
|
|
237
|
-
framework: "playwright",
|
|
238
|
-
modularizeCode: false,
|
|
239
|
-
prompt: "Example code",
|
|
240
|
-
};
|
|
241
|
-
const result = await toolHandler(params);
|
|
242
|
-
const outputText = result.content[0].text;
|
|
243
|
-
expect(outputText).toContain(mockPromptContent);
|
|
244
|
-
expect(outputText).toContain("Read test file /test/example.ts");
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
describe("Tool Metadata", () => {
|
|
248
|
-
it("should have appropriate keywords for discoverability", () => {
|
|
249
|
-
registerCodeReuseTool(mockServer);
|
|
250
|
-
const toolConfig = mockRegisterTool.mock.calls[0][1];
|
|
251
|
-
const keywords = toolConfig.annotations.keywords;
|
|
252
|
-
expect(keywords).toContain("code reuse");
|
|
253
|
-
expect(keywords).toContain("duplicate code");
|
|
254
|
-
expect(keywords).toContain("helper functions");
|
|
255
|
-
expect(keywords).toContain("import");
|
|
256
|
-
expect(keywords).toContain("refactor");
|
|
257
|
-
});
|
|
258
|
-
it("should have a descriptive tool description", () => {
|
|
259
|
-
registerCodeReuseTool(mockServer);
|
|
260
|
-
const toolConfig = mockRegisterTool.mock.calls[0][1];
|
|
261
|
-
expect(toolConfig.description).toContain("Analyzes code for reuse opportunities");
|
|
262
|
-
expect(toolConfig.description).toContain("duplicate code patterns");
|
|
263
|
-
expect(toolConfig.description).toContain("importing and reusing existing helper functions");
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
});
|