@mablhq/mabl-cli 2.65.3 → 2.67.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/api/mablApiClient.js +8 -0
- package/browserLauncher/index.js +1 -1
- package/cli.js +1 -0
- package/commands/mcp/mcp.js +5 -0
- package/commands/mcp/mcp_cmds/start.js +78 -0
- package/commands/mcp/mcp_cmds/tools/analyzeFailure.js +30 -0
- package/commands/mcp/mcp_cmds/tools/getEnvironments.js +38 -0
- package/commands/mcp/mcp_cmds/tools/getRecentFailures.js +22 -0
- package/commands/mcp/mcp_cmds/tools/getResults.js +42 -0
- package/commands/mcp/mcp_cmds/tools/getTests.js +41 -0
- package/commands/mcp/mcp_cmds/tools/index.js +19 -0
- package/commands/mcp/mcp_cmds/tools/mcpMablTool.js +6 -0
- package/commands/mcp/mcp_cmds/tools/runTest.js +54 -0
- package/commands/tests/tests_cmds/run.js +6 -4
- package/execution/index.js +1 -1
- package/package.json +5 -2
- package/providers/logging/loggingProvider.js +20 -13
package/cli.js
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const util_1 = require("../../commandUtil/util");
|
|
7
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
8
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
10
|
+
const loggingProvider_1 = require("../../../providers/logging/loggingProvider");
|
|
11
|
+
const tools_1 = __importDefault(require("./tools"));
|
|
12
|
+
const constants_1 = require("../../constants");
|
|
13
|
+
exports.command = 'start [options]';
|
|
14
|
+
exports.describe = 'Start the MCP server';
|
|
15
|
+
exports.builder = (yargs) => {
|
|
16
|
+
yargs
|
|
17
|
+
.option(constants_1.CommandArgApiKey, {
|
|
18
|
+
alias: constants_1.CommandArgAliases.ApiKey,
|
|
19
|
+
describe: 'API key (found in the mabl web app)',
|
|
20
|
+
nargs: 1,
|
|
21
|
+
demand: 'API key argument required',
|
|
22
|
+
type: 'string',
|
|
23
|
+
})
|
|
24
|
+
.example('$0 mcp server', 'Start the MCP server');
|
|
25
|
+
};
|
|
26
|
+
exports.handler = (0, util_1.failWrapper)(startServer);
|
|
27
|
+
async function startServer(parsed) {
|
|
28
|
+
(0, loggingProvider_1.disableLogging)();
|
|
29
|
+
const server = new index_js_1.Server({
|
|
30
|
+
name: 'mabl',
|
|
31
|
+
version: '1.0.0',
|
|
32
|
+
}, {
|
|
33
|
+
capabilities: {
|
|
34
|
+
tools: {},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => ({
|
|
38
|
+
tools: tools_1.default.map((tool) => ({
|
|
39
|
+
name: tool.schema.name,
|
|
40
|
+
description: tool.schema.description,
|
|
41
|
+
inputSchema: tool.schema.inputSchema.safeParse({}).success
|
|
42
|
+
? { type: 'object', properties: {} }
|
|
43
|
+
: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: Object.fromEntries(Object.entries(tool.schema.inputSchema.shape).map(([key, value]) => {
|
|
46
|
+
const description = value._def.description;
|
|
47
|
+
return [
|
|
48
|
+
key,
|
|
49
|
+
{
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: description || '',
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
})),
|
|
55
|
+
},
|
|
56
|
+
})),
|
|
57
|
+
}));
|
|
58
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
59
|
+
const { name } = request.params;
|
|
60
|
+
const tool = tools_1.default.find((t) => t.schema.name === name);
|
|
61
|
+
if (!tool) {
|
|
62
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const args = request.params.arguments || {};
|
|
66
|
+
const result = await tool.handle(args, parsed[constants_1.CommandArgApiKey]);
|
|
67
|
+
return {
|
|
68
|
+
content: result.content,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
76
|
+
await server.connect(transport);
|
|
77
|
+
console.error('Mabl MCP Server running on stdio');
|
|
78
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const mcpMablTool_1 = require("./mcpMablTool");
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const analyzeFailure = (0, mcpMablTool_1.defineTool)({
|
|
6
|
+
schema: {
|
|
7
|
+
name: 'analyze-failure',
|
|
8
|
+
title: 'Analyze a mabl test execution failure',
|
|
9
|
+
description: 'Analyze a mabl test execution failure',
|
|
10
|
+
inputSchema: zod_1.z.object({
|
|
11
|
+
runId: zod_1.z.string().describe('The runId of the failed test run to analyze'),
|
|
12
|
+
}),
|
|
13
|
+
type: 'readOnly',
|
|
14
|
+
},
|
|
15
|
+
handle: (params) => {
|
|
16
|
+
const runId = params.runId;
|
|
17
|
+
const failureSynopsis = 'Failed at Step 6: The text on the page changed, causing an assertion failure.';
|
|
18
|
+
const failureSummary = 'The test is failing because the text on the page has changed from "step 1: create a test!" to "step 1: create a journey!". The assertion in the test is expecting the original text, which is why the test is failing. It appears that the application under test has been updated so that it now displays "step 1: create a journey!" instead of "step 1: create a test!". To resolve this failure, you should update the assertion in the test to expect the new text, "step 1: create a journey!".';
|
|
19
|
+
const summary = `RunId: ${runId} Status: failed FailureSynopsis: ${failureSynopsis} FailureDetails: ${failureSummary}`;
|
|
20
|
+
return Promise.resolve({
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: 'text',
|
|
24
|
+
text: summary,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
exports.default = [analyzeFailure];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const util_1 = require("../../../commandUtil/util");
|
|
4
|
+
const mcpMablTool_1 = require("./mcpMablTool");
|
|
5
|
+
const mablApiClientFactory_1 = require("../../../../api/mablApiClientFactory");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const getEnvironments = (0, mcpMablTool_1.defineTool)({
|
|
8
|
+
schema: {
|
|
9
|
+
name: 'get-environments',
|
|
10
|
+
title: 'Get all mabl environments',
|
|
11
|
+
description: 'Get all mabl environments',
|
|
12
|
+
inputSchema: zod_1.z.object({}),
|
|
13
|
+
type: 'readOnly',
|
|
14
|
+
},
|
|
15
|
+
handle: async (_, apiKey) => {
|
|
16
|
+
const arg = {
|
|
17
|
+
'workspace-id': undefined,
|
|
18
|
+
output: 'json',
|
|
19
|
+
_: [],
|
|
20
|
+
$0: '',
|
|
21
|
+
};
|
|
22
|
+
const workspaceId = await (0, util_1.getWorkspaceId)(arg);
|
|
23
|
+
const limit = 500;
|
|
24
|
+
const apiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromOptionalApiKey(apiKey);
|
|
25
|
+
const environments = await apiClient.getEnvironments(workspaceId, limit);
|
|
26
|
+
return {
|
|
27
|
+
content: [
|
|
28
|
+
{
|
|
29
|
+
type: 'text',
|
|
30
|
+
text: environments
|
|
31
|
+
.map((env) => `Name: ${env.name} ID: ${env.id}`)
|
|
32
|
+
.join('\n'),
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
exports.default = [getEnvironments];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const mcpMablTool_1 = require("./mcpMablTool");
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const getRecentFailures = (0, mcpMablTool_1.defineTool)({
|
|
6
|
+
schema: {
|
|
7
|
+
name: 'get-recent-failures',
|
|
8
|
+
title: 'Get mabl tests with recent failures',
|
|
9
|
+
description: 'Get mabl tests with recent failures',
|
|
10
|
+
inputSchema: zod_1.z.object({}),
|
|
11
|
+
type: 'readOnly',
|
|
12
|
+
},
|
|
13
|
+
handle: () => Promise.resolve({
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
type: 'text',
|
|
17
|
+
text: 'Test: Sandbox training example ID: Pc0Kvc5gbqeDTWgf23LGLg-j Count: 8',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
exports.default = [getRecentFailures];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const mcpMablTool_1 = require("./mcpMablTool");
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const mablApiClientFactory_1 = require("../../../../api/mablApiClientFactory");
|
|
6
|
+
const getResults = (0, mcpMablTool_1.defineTool)({
|
|
7
|
+
schema: {
|
|
8
|
+
name: 'get-results',
|
|
9
|
+
title: 'Get recent mabl test run results',
|
|
10
|
+
description: 'Get recent mabl test run results',
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
testId: zod_1.z
|
|
13
|
+
.string()
|
|
14
|
+
.describe('The id of the mabl test to get recent results of'),
|
|
15
|
+
}),
|
|
16
|
+
type: 'readOnly',
|
|
17
|
+
},
|
|
18
|
+
handle: async (params, apiKey) => {
|
|
19
|
+
const apiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromOptionalApiKey(apiKey);
|
|
20
|
+
const testId = params.testId;
|
|
21
|
+
const testRunResults = (await apiClient.getRecentTestRunsForTest(testId))
|
|
22
|
+
.test_script_executions || [];
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: 'text',
|
|
27
|
+
text: testRunResults
|
|
28
|
+
.map((testRun) => {
|
|
29
|
+
const id = testRun.id;
|
|
30
|
+
const completedTimeMs = testRun.completed_time;
|
|
31
|
+
const status = testRun.status;
|
|
32
|
+
const envId = testRun.journey_parameters?.deployment?.environment_id;
|
|
33
|
+
const errorMessage = testRun.failure_summary?.error;
|
|
34
|
+
return `TestRunId: ${id} EnvironmentId: ${envId} Status: ${status} CompletedTimeMs: ${completedTimeMs} ErrorMessage: ${errorMessage}`;
|
|
35
|
+
})
|
|
36
|
+
.join('\n'),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
exports.default = [getResults];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const util_1 = require("../../../commandUtil/util");
|
|
4
|
+
const mcpMablTool_1 = require("./mcpMablTool");
|
|
5
|
+
const mablApiClientFactory_1 = require("../../../../api/mablApiClientFactory");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const getTests = (0, mcpMablTool_1.defineTool)({
|
|
8
|
+
schema: {
|
|
9
|
+
name: 'get-tests',
|
|
10
|
+
title: 'Get all mabl tests',
|
|
11
|
+
description: 'Get all mabl tests',
|
|
12
|
+
inputSchema: zod_1.z.object({}),
|
|
13
|
+
type: 'readOnly',
|
|
14
|
+
},
|
|
15
|
+
handle: async (_, apiKey) => {
|
|
16
|
+
const arg = {
|
|
17
|
+
'workspace-id': undefined,
|
|
18
|
+
output: 'json',
|
|
19
|
+
_: [],
|
|
20
|
+
$0: '',
|
|
21
|
+
};
|
|
22
|
+
const workspaceId = await (0, util_1.getWorkspaceId)(arg);
|
|
23
|
+
const limit = 50;
|
|
24
|
+
const apiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromOptionalApiKey(apiKey);
|
|
25
|
+
const tests = await apiClient.getJourneys({
|
|
26
|
+
organization_id: workspaceId,
|
|
27
|
+
limit,
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
content: [
|
|
31
|
+
{
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: tests
|
|
34
|
+
.map((test) => `Test: ${test.name} ID: ${test.invariant_id}`)
|
|
35
|
+
.join('\n'),
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
exports.default = [getTests];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const getEnvironments_1 = __importDefault(require("./getEnvironments"));
|
|
7
|
+
const getTests_1 = __importDefault(require("./getTests"));
|
|
8
|
+
const getRecentFailures_1 = __importDefault(require("./getRecentFailures"));
|
|
9
|
+
const getResults_1 = __importDefault(require("./getResults"));
|
|
10
|
+
const analyzeFailure_1 = __importDefault(require("./analyzeFailure"));
|
|
11
|
+
const runTest_1 = __importDefault(require("./runTest"));
|
|
12
|
+
exports.default = [
|
|
13
|
+
...getEnvironments_1.default,
|
|
14
|
+
...getTests_1.default,
|
|
15
|
+
...getRecentFailures_1.default,
|
|
16
|
+
...getResults_1.default,
|
|
17
|
+
...analyzeFailure_1.default,
|
|
18
|
+
...runTest_1.default,
|
|
19
|
+
];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const mcpMablTool_1 = require("./mcpMablTool");
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const run_1 = require("../../../tests/tests_cmds/run");
|
|
6
|
+
const mablApiClientFactory_1 = require("../../../../api/mablApiClientFactory");
|
|
7
|
+
const runTest = (0, mcpMablTool_1.defineTool)({
|
|
8
|
+
schema: {
|
|
9
|
+
name: 'run-test',
|
|
10
|
+
title: 'Run a mabl test',
|
|
11
|
+
description: 'Run a mabl test',
|
|
12
|
+
inputSchema: zod_1.z.object({
|
|
13
|
+
environmentId: zod_1.z
|
|
14
|
+
.string()
|
|
15
|
+
.describe('The id of the environment to run the test in'),
|
|
16
|
+
testId: zod_1.z.string().describe('The id of the test to run'),
|
|
17
|
+
}),
|
|
18
|
+
type: 'destructive',
|
|
19
|
+
},
|
|
20
|
+
handle: async (params, apiKey) => {
|
|
21
|
+
const mablApiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromOptionalApiKey(apiKey);
|
|
22
|
+
const result = await (0, run_1.run)({
|
|
23
|
+
id: params.testId,
|
|
24
|
+
'environment-id': params.environmentId || '',
|
|
25
|
+
branch: 'master',
|
|
26
|
+
'branch-changes-only': false,
|
|
27
|
+
browser: 'chrome',
|
|
28
|
+
'browser-disable-isolation': false,
|
|
29
|
+
'browser-ignore-certificate-errors': false,
|
|
30
|
+
'enable-link': false,
|
|
31
|
+
headless: false,
|
|
32
|
+
height: 1024,
|
|
33
|
+
highlights: false,
|
|
34
|
+
'keep-browser-open': false,
|
|
35
|
+
'run-id': undefined,
|
|
36
|
+
width: 1024,
|
|
37
|
+
output: 'json',
|
|
38
|
+
url: 'http://sandbox-local.mabl.com:3000',
|
|
39
|
+
_: [],
|
|
40
|
+
$0: '',
|
|
41
|
+
}, false, mablApiClient);
|
|
42
|
+
return {
|
|
43
|
+
content: [
|
|
44
|
+
{
|
|
45
|
+
type: 'text',
|
|
46
|
+
text: `Test run ${result.success
|
|
47
|
+
? 'succeeded'
|
|
48
|
+
: `failed ${result.testResults[0].errorMessage}`}`,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
exports.default = [runTest];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.run = run;
|
|
3
4
|
const util_1 = require("../../commandUtil/util");
|
|
4
5
|
const testsUtil_1 = require("../testsUtil");
|
|
5
6
|
const constants_1 = require("../../constants");
|
|
@@ -218,7 +219,7 @@ Note: Setting the environment does not override the default URL. Please use the
|
|
|
218
219
|
};
|
|
219
220
|
const exitCodeOnError = 1;
|
|
220
221
|
exports.handler = (0, util_1.failWrapper)(run, exitCodeOnError);
|
|
221
|
-
async function run(parsed) {
|
|
222
|
+
async function run(parsed, killProcessOnFailure = true, apiClient) {
|
|
222
223
|
const commandStartTime = Date.now();
|
|
223
224
|
let workspaceId;
|
|
224
225
|
try {
|
|
@@ -236,7 +237,7 @@ async function run(parsed) {
|
|
|
236
237
|
const browserType = (0, testsUtil_1.parseBrowserType)(parsed[constants_1.CommandArgBrowser]);
|
|
237
238
|
let scenario;
|
|
238
239
|
if (parsed[constants_1.CommandArgScenarioId]) {
|
|
239
|
-
|
|
240
|
+
apiClient ??= await mablApiClientFactory_1.MablApiClientFactory.createApiClient();
|
|
240
241
|
scenario = await apiClient.getScenario(parsed[constants_1.CommandArgScenarioId]);
|
|
241
242
|
if (!scenario) {
|
|
242
243
|
throw new Error(`Scenario with ID ${parsed[constants_1.CommandArgScenarioId]} not found`);
|
|
@@ -295,13 +296,14 @@ async function run(parsed) {
|
|
|
295
296
|
(0, testsUtil_1.cleanupTestResources)(testContext);
|
|
296
297
|
});
|
|
297
298
|
}
|
|
298
|
-
|
|
299
|
+
apiClient ??= await mablApiClientFactory_1.MablApiClientFactory.createApiClient();
|
|
299
300
|
await (0, runUtils_1.logTestResults)(results, parsed, commandStartTime, generateRunCommandTemplate(parsed, results), apiClient, browserType, parsed[constants_1.CommandArgVerbose]);
|
|
300
301
|
if (!testRunnerConfig.keepBrowserOpen) {
|
|
301
|
-
if (!results.success) {
|
|
302
|
+
if (!results.success && killProcessOnFailure) {
|
|
302
303
|
process.exit(1);
|
|
303
304
|
}
|
|
304
305
|
}
|
|
306
|
+
return results;
|
|
305
307
|
}
|
|
306
308
|
function generateRunCommandTemplate(parsed, testResults) {
|
|
307
309
|
const testResult = testResults.testResults[0];
|