@mablhq/mabl-cli 2.70.1 → 2.70.10

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.
@@ -11,6 +11,7 @@ const loggingProvider_1 = require("../../../providers/logging/loggingProvider");
11
11
  const tools_1 = __importDefault(require("./tools"));
12
12
  const constants_1 = require("../../constants");
13
13
  const createMablTestForCurrentWork_1 = require("./prompts/createMablTestForCurrentWork");
14
+ const common_1 = require("./tools/common");
14
15
  exports.command = 'start [options]';
15
16
  exports.describe = 'Start the MCP server';
16
17
  exports.builder = (yargs) => {
@@ -40,10 +41,8 @@ async function startServer(parsed) {
40
41
  },
41
42
  });
42
43
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => ({
43
- tools: tools_1.default.map((tool) => ({
44
- name: tool.schema.name,
45
- description: tool.schema.description,
46
- inputSchema: tool.schema.inputSchema.safeParse({}).success
44
+ tools: tools_1.default.map((tool) => {
45
+ const inputSchema = tool.schema.inputSchema.safeParse({}).success
47
46
  ? { type: 'object', properties: {} }
48
47
  : {
49
48
  type: 'object',
@@ -57,8 +56,16 @@ async function startServer(parsed) {
57
56
  },
58
57
  ];
59
58
  })),
60
- },
61
- })),
59
+ };
60
+ return {
61
+ name: tool.schema.name,
62
+ description: tool.schema.description,
63
+ inputSchema,
64
+ ...(tool.schema.outputSchema
65
+ ? { outputSchema: (0, common_1.zodSchemaToOpenApiSchema)(tool.schema.outputSchema) }
66
+ : {}),
67
+ };
68
+ }),
62
69
  }));
63
70
  server.setRequestHandler(types_js_1.ListPromptsRequestSchema, () => ({
64
71
  prompts: [createMablTestForCurrentWork_1.createMablTestPrompt],
@@ -16,6 +16,17 @@ const analyzeFailure = (0, mcpMablTool_1.defineTool)({
16
16
  },
17
17
  handle: async (params, apiKey, server) => {
18
18
  const runId = params.runId;
19
+ if (runId.endsWith('-j')) {
20
+ return {
21
+ content: [
22
+ {
23
+ type: 'text',
24
+ text: `The runId ${runId} appears to be a test id. Please call the getResults tool with this id first, then call analyze-failure again with the resulting first runId with status "Failed".`,
25
+ isError: false,
26
+ },
27
+ ],
28
+ };
29
+ }
19
30
  const mablApiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromOptionalApiKey(apiKey);
20
31
  const workspaceId = (await mablApiClient.getApiKeyDetails()).workspace_id;
21
32
  if (!workspaceId) {
@@ -1,13 +1,74 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.stringToToolResult = stringToToolResult;
4
- function stringToToolResult(value) {
4
+ exports.zodSchemaToOpenApiSchema = zodSchemaToOpenApiSchema;
5
+ const zod_1 = require("zod");
6
+ function stringToToolResult(value, isError = false) {
5
7
  return {
6
8
  content: [
7
9
  {
8
10
  type: 'text',
9
11
  text: value,
12
+ isError,
10
13
  },
11
14
  ],
12
15
  };
13
16
  }
17
+ function zodSchemaToOpenApiSchema(schema) {
18
+ if (schema instanceof zod_1.z.ZodArray) {
19
+ const element = schema.element;
20
+ if (element instanceof zod_1.z.ZodObject) {
21
+ const itemShape = element.shape;
22
+ return {
23
+ type: 'array',
24
+ items: {
25
+ type: 'object',
26
+ properties: Object.fromEntries(Object.entries(itemShape).map(([key, value]) => {
27
+ const description = value._def.description;
28
+ return [
29
+ key,
30
+ {
31
+ type: (() => {
32
+ if (value instanceof zod_1.z.ZodString) {
33
+ return 'string';
34
+ }
35
+ if (value instanceof zod_1.z.ZodNumber) {
36
+ return 'number';
37
+ }
38
+ if (value instanceof zod_1.z.ZodBoolean) {
39
+ return 'boolean';
40
+ }
41
+ if (value instanceof zod_1.z.ZodArray) {
42
+ return 'array';
43
+ }
44
+ if (value instanceof zod_1.z.ZodObject) {
45
+ return 'object';
46
+ }
47
+ return 'string';
48
+ })(),
49
+ description: description || '',
50
+ },
51
+ ];
52
+ })),
53
+ },
54
+ };
55
+ }
56
+ }
57
+ else if (schema instanceof zod_1.z.ZodObject) {
58
+ const shape = schema.shape;
59
+ return {
60
+ type: 'object',
61
+ properties: Object.fromEntries(Object.entries(shape).map(([key, value]) => {
62
+ const description = value._def.description;
63
+ return [
64
+ key,
65
+ {
66
+ type: 'string',
67
+ description: description || '',
68
+ },
69
+ ];
70
+ })),
71
+ };
72
+ }
73
+ return undefined;
74
+ }
@@ -5,16 +5,20 @@ const zod_1 = require("zod");
5
5
  const mablApiClientFactory_1 = require("../../../../api/mablApiClientFactory");
6
6
  const trainingSessions_1 = require("../../../../core/trainer/trainingSessions");
7
7
  const messaging_1 = require("../../../../core/messaging/messaging");
8
+ const mablApi_1 = require("../../../../mablApi");
9
+ const common_1 = require("./common");
8
10
  const runTest = (0, mcpMablTool_1.defineTool)({
9
11
  schema: {
10
12
  name: 'create-test',
11
13
  title: 'Create a mabl test',
12
- description: 'Create a mabl test',
14
+ description: 'Create a mabl browser or API test',
13
15
  inputSchema: zod_1.z.object({
14
16
  environmentId: zod_1.z
15
17
  .string()
16
18
  .describe('The id of the environment to run the test in'),
17
- url: zod_1.z.string().describe('The url of the application to test'),
19
+ url: zod_1.z
20
+ .string()
21
+ .describe('The url of the application to test. On API tests this will be the endpoint to test.'),
18
22
  name: zod_1.z.string().describe('The name of the test'),
19
23
  intent: zod_1.z.string().describe(`The intent of the test.
20
24
  Be as detailed as possible.
@@ -24,17 +28,29 @@ const runTest = (0, mcpMablTool_1.defineTool)({
24
28
  issueOrTicket: zod_1.z
25
29
  .string()
26
30
  .describe('The issue or ticket description that is being worked on. This will be used to generate the intent.'),
27
- steps: zod_1.z
28
- .string()
29
- .describe('List of steps to accomplish the intent. Put each step in a new line.'),
31
+ steps: zod_1.z.string().describe(`List of steps to accomplish the intent. Put each step in a new line.
32
+ If you are testing an API, include data to be sent in the request and the expected response.
33
+ `),
30
34
  intentUrl: zod_1.z
31
35
  .string()
32
36
  .describe('Optional URL to be included in the intent')
33
37
  .optional(),
38
+ isApiTest: zod_1.z
39
+ .boolean()
40
+ .describe('Whether the test is an API test. If true, the test will be created as an API test. False if the intent is unknown.'),
41
+ apiSpec: zod_1.z
42
+ .string()
43
+ .describe(`Optional API spec to be included in the intent.
44
+ If provided, the test will be created as an API test.
45
+ This is required when creating an API test.`)
46
+ .optional(),
34
47
  }),
35
48
  type: 'destructive',
36
49
  },
37
50
  handle: async (params, apiKey) => {
51
+ if (params.isApiTest && !params.apiSpec) {
52
+ return (0, common_1.stringToToolResult)('apiSpec is required when creating an API test.', true);
53
+ }
38
54
  const mablApiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromOptionalApiKey(apiKey);
39
55
  const workspaceId = (await mablApiClient.getApiKeyDetails()).workspace_id;
40
56
  messaging_1.mablEventEmitter.resetOutputEventChannel();
@@ -48,6 +64,9 @@ const runTest = (0, mcpMablTool_1.defineTool)({
48
64
  if (params.issueOrTicket) {
49
65
  fullIntent += `\n\nIssue or ticket: ${params.issueOrTicket}`;
50
66
  }
67
+ if (params.apiSpec) {
68
+ fullIntent += `\n\nAPI spec: ${params.apiSpec}`;
69
+ }
51
70
  await (0, trainingSessions_1.trainNewTest)({
52
71
  environmentId: params.environmentId,
53
72
  url: params.url,
@@ -56,6 +75,8 @@ const runTest = (0, mcpMablTool_1.defineTool)({
56
75
  autoLogin: false,
57
76
  workspaceId,
58
77
  intent: fullIntent,
78
+ testType: params.isApiTest ? mablApi_1.TestTypeEnum.Api : mablApi_1.TestTypeEnum.Browser,
79
+ apiDescription: params.apiSpec,
59
80
  }, mablApiClient, false);
60
81
  return {
61
82
  content: [
@@ -15,15 +15,15 @@ exports.environmentRequiredToolResult = (0, common_1.stringToToolResult)(`
15
15
  Tell the user that the environmentId is required to export the test to Playwright with good locators.
16
16
  Call the get-environments tool to get the list of environments for the test.
17
17
  Ask them to select the environmentId from the list of environments and then call this tool again with the environmentId.
18
- `);
18
+ `, true);
19
19
  exports.performanceTestToolResult = (0, common_1.stringToToolResult)(`
20
20
  ## Instructions
21
21
  Tell the user that the test provided is a performance test and it can't be exported to playwright.
22
- `);
22
+ `, true);
23
23
  exports.defaultTestToolResult = (0, common_1.stringToToolResult)(`
24
24
  ## Instructions
25
25
  Tell the user that default tests can not be exported to playwright.
26
- `);
26
+ `, true);
27
27
  const exportToPlaywrightSchema = zod_1.z.object({
28
28
  testId: zod_1.z
29
29
  .string()
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const mcpMablTool_1 = require("./mcpMablTool");
4
4
  const zod_1 = require("zod");
5
5
  const mablApiClientFactory_1 = require("../../../../api/mablApiClientFactory");
6
+ const mablApi_1 = require("../../../../mablApi");
7
+ const common_1 = require("./common");
6
8
  const getResults = (0, mcpMablTool_1.defineTool)({
7
9
  schema: {
8
10
  name: 'get-results',
@@ -14,28 +16,71 @@ const getResults = (0, mcpMablTool_1.defineTool)({
14
16
  .describe('The id of the mabl test to get recent results of'),
15
17
  }),
16
18
  type: 'readOnly',
19
+ outputSchema: zod_1.z.array(zod_1.z.object({
20
+ testRunId: zod_1.z.string().nullable(),
21
+ environmentId: zod_1.z.string().nullable(),
22
+ status: zod_1.z.string().nullable(),
23
+ completedTimeMs: zod_1.z.number().nullable(),
24
+ errorMessage: zod_1.z.string(),
25
+ })),
17
26
  },
18
27
  handle: async (params, apiKey) => {
19
28
  const apiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromOptionalApiKey(apiKey);
20
29
  const testId = params.testId;
21
30
  const testRunResults = (await apiClient.getRecentTestRunsForTest(testId))
22
31
  .test_script_executions || [];
32
+ const workspaceIdMaybe = (await apiClient.getApiKeyDetails()).workspace_id;
33
+ if (!workspaceIdMaybe) {
34
+ return (0, common_1.stringToToolResult)('Workspace ID not found', true);
35
+ }
36
+ const workspaceId = workspaceIdMaybe;
37
+ const processedResults = await Promise.all(testRunResults.map(async (testRun) => {
38
+ let errorMessage = '';
39
+ if (testRun.status === mablApi_1.TestRun.StatusEnum.Failed &&
40
+ testRun.failure_categorization?.is_failure_category_generated !== true) {
41
+ try {
42
+ const analysis = await apiClient.analyzeTestRun(workspaceId, {
43
+ test_run_id: testRun.id,
44
+ });
45
+ testRun.failure_categorization = {
46
+ ...testRun.failure_categorization,
47
+ ...analysis,
48
+ };
49
+ }
50
+ catch (e) {
51
+ console.error('Error analyzing test run:', e);
52
+ errorMessage = `Analysis error: ${e instanceof Error ? e.message : String(e)}`;
53
+ }
54
+ }
55
+ if (testRun.failure_categorization) {
56
+ errorMessage =
57
+ testRun.failure_categorization.failure_summary_text ??
58
+ testRun.failure_summary?.error ??
59
+ '';
60
+ }
61
+ if (!errorMessage) {
62
+ errorMessage = 'No categorized error available.';
63
+ }
64
+ const id = testRun.id;
65
+ const completedTimeMs = testRun.completed_time;
66
+ const status = testRun.status;
67
+ const envId = testRun.journey_parameters?.deployment?.environment_id;
68
+ return {
69
+ testRunId: id,
70
+ environmentId: envId,
71
+ status,
72
+ completedTimeMs,
73
+ errorMessage,
74
+ };
75
+ }));
23
76
  return {
24
77
  content: [
25
78
  {
26
79
  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'),
80
+ text: JSON.stringify(processedResults),
37
81
  },
38
82
  ],
83
+ structuredContent: processedResults,
39
84
  };
40
85
  },
41
86
  });
@@ -1498,6 +1498,8 @@ function postmanAuthToApiTestAuth(auth) {
1498
1498
  verifier: getValueFromVariablesForAuthType('verifier'),
1499
1499
  };
1500
1500
  break;
1501
+ default:
1502
+ return undefined;
1501
1503
  }
1502
1504
  return apiTestAuth;
1503
1505
  }
@@ -27,7 +27,7 @@ async function trainNewTest(trainingSessionOptions, mablApiClient, waitProcessTo
27
27
  (await (0, util_1.getApiClientFromOptions)(trainingSessionOptions, true));
28
28
  const branchName = trainingSessionOptions.branchName ?? constants_1.DefaultBranchName;
29
29
  const { authToken, autoBranch, dataTableIds, intent: testIntent, ...passThroughFields } = trainingSessionOptions;
30
- const { applicationId, autoLogin, credentialsId, environmentId, height, planId, testDescription, testName, url, width, workspaceId, } = passThroughFields;
30
+ const { apiDescription, applicationId, autoLogin, credentialsId, environmentId, height, planId, testDescription, testName, url, width, testType, workspaceId, } = passThroughFields;
31
31
  if (branchName !== 'master') {
32
32
  await (0, branches_1.checkBranchNameAndAutoBranchMaybe)(workspaceId, apiClient, branchName, autoBranch);
33
33
  }
@@ -62,6 +62,8 @@ async function trainNewTest(trainingSessionOptions, mablApiClient, waitProcessTo
62
62
  (0, testsUtil_1.logTestInfoIfPresent)(`Credentials ID: `, credentialsId);
63
63
  (0, testsUtil_1.logTestInfoIfPresent)(`Datatable ID(s): `, dataTableIds?.join(', '));
64
64
  (0, testsUtil_1.logTestInfoIfPresent)(`Test description: `, testDescription);
65
+ (0, testsUtil_1.logTestInfoIfPresent)(`Test type: `, testType);
66
+ (0, testsUtil_1.logTestInfoIfPresent)(`API description: `, apiDescription);
65
67
  const scriptConfig = {
66
68
  ...passThroughFields,
67
69
  accountId: account.id,