@browserstack/mcp-server 1.2.29-beta.1 → 1.2.30

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,20 @@ export declare function normalizeDefaultFieldValue(fieldValues: Array<{
16
16
  name?: string;
17
17
  value: any;
18
18
  }>, input: string, emit: "name" | "internal_name"): string | undefined;
19
+ export interface DefaultFieldInputs {
20
+ priority?: string;
21
+ case_type?: string;
22
+ automation_status?: string;
23
+ }
24
+ /**
25
+ * Normalize priority/case_type/automation_status against a project's already
26
+ * fetched `default_fields`. priority and case_type resolve to the display name
27
+ * (the create/update endpoints reject lowercase internal names); on no match
28
+ * the raw value passes through. automation_status is special: its values carry
29
+ * a null internal_name and hold the internal name in `value`, so it matches on
30
+ * name-or-value and emits `value`. Callers own the fetch and its error path.
31
+ */
32
+ export declare function normalizeDefaultFields(defaultFields: any, inputs: DefaultFieldInputs): DefaultFieldInputs;
19
33
  /**
20
34
  * Trigger AI-based test case generation for a document.
21
35
  */
@@ -30,6 +30,33 @@ export function normalizeDefaultFieldValue(fieldValues, input, emit) {
30
30
  return match.name;
31
31
  return match.internal_name ?? match.name;
32
32
  }
33
+ /**
34
+ * Normalize priority/case_type/automation_status against a project's already
35
+ * fetched `default_fields`. priority and case_type resolve to the display name
36
+ * (the create/update endpoints reject lowercase internal names); on no match
37
+ * the raw value passes through. automation_status is special: its values carry
38
+ * a null internal_name and hold the internal name in `value`, so it matches on
39
+ * name-or-value and emits `value`. Callers own the fetch and its error path.
40
+ */
41
+ export function normalizeDefaultFields(defaultFields, inputs) {
42
+ const out = {};
43
+ if (inputs.priority !== undefined) {
44
+ out.priority =
45
+ normalizeDefaultFieldValue(defaultFields?.priority?.values ?? [], inputs.priority, "name") ?? inputs.priority;
46
+ }
47
+ if (inputs.case_type !== undefined) {
48
+ out.case_type =
49
+ normalizeDefaultFieldValue(defaultFields?.case_type?.values ?? [], inputs.case_type, "name") ?? inputs.case_type;
50
+ }
51
+ if (inputs.automation_status !== undefined) {
52
+ const values = defaultFields?.automation_status?.values ?? [];
53
+ const input = inputs.automation_status.toLowerCase().trim();
54
+ const match = values.find((v) => (v.value ?? "").toLowerCase() === input ||
55
+ (v.name ?? "").toLowerCase() === input);
56
+ out.automation_status = match?.value ?? inputs.automation_status;
57
+ }
58
+ return out;
59
+ }
33
60
  /**
34
61
  * Trigger AI-based test case generation for a document.
35
62
  */
@@ -24,6 +24,7 @@ export interface TestCaseCreateRequest {
24
24
  custom_fields?: Record<string, CustomFieldValue>;
25
25
  automation_status?: string;
26
26
  priority?: string;
27
+ case_type?: string;
27
28
  template?: string;
28
29
  template_id?: number;
29
30
  }
@@ -77,6 +78,7 @@ export declare const CreateTestCaseSchema: z.ZodObject<{
77
78
  custom_fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>]>>>;
78
79
  automation_status: z.ZodOptional<z.ZodString>;
79
80
  priority: z.ZodOptional<z.ZodString>;
81
+ case_type: z.ZodOptional<z.ZodString>;
80
82
  template: z.ZodOptional<z.ZodString>;
81
83
  template_id: z.ZodOptional<z.ZodNumber>;
82
84
  }, z.core.$strip>;
@@ -1,7 +1,7 @@
1
1
  import { apiClient } from "../../lib/apiClient.js";
2
2
  import { z } from "zod";
3
3
  import { formatAxiosError } from "../../lib/error.js";
4
- import { fetchFormFields, normalizeDefaultFieldValue, projectIdentifierToId, } from "./TCG-utils/api.js";
4
+ import { fetchFormFields, normalizeDefaultFields as normalizeDefaultFieldsFromForm, projectIdentifierToId, } from "./TCG-utils/api.js";
5
5
  import { getTMBaseURL } from "../../lib/tm-base-url.js";
6
6
  import logger from "../../logger.js";
7
7
  // Scalar value, or an array of values for multi-select custom fields.
@@ -66,6 +66,10 @@ export const CreateTestCaseSchema = z.object({
66
66
  .string()
67
67
  .optional()
68
68
  .describe("Priority of the test case. Accepts either display name (e.g. 'Critical', 'High', 'Medium', 'Low') or internal name (e.g. 'medium'). If omitted, the project default (usually 'Medium') is applied. Valid values are per-project and discoverable via the form-fields endpoint."),
69
+ case_type: z
70
+ .string()
71
+ .optional()
72
+ .describe("Test case type display or internal name (per-project). Omit for project default."),
69
73
  template: z
70
74
  .string()
71
75
  .optional()
@@ -99,18 +103,19 @@ export function sanitizeArgs(args) {
99
103
  }
100
104
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
101
105
  /**
102
- * Normalize priority to the display name the create endpoint accepts (it
103
- * rejects lowercase). On lookup failure, pass the raw value through.
106
+ * Fetch the project's form-fields and normalize priority/case_type to the
107
+ * display names the create endpoint accepts (it rejects lowercase internal
108
+ * names). On lookup failure, passes raw values through.
104
109
  */
105
- async function normalizePriority(projectIdentifier, priority, config) {
110
+ async function normalizeDefaultFields(projectIdentifier, fields, config) {
106
111
  try {
107
112
  const numericProjectId = await projectIdentifierToId(projectIdentifier, config);
108
113
  const { default_fields } = await fetchFormFields(numericProjectId, config);
109
- return (normalizeDefaultFieldValue(default_fields?.priority?.values ?? [], priority, "name") ?? priority);
114
+ return normalizeDefaultFieldsFromForm(default_fields, fields);
110
115
  }
111
116
  catch (err) {
112
- logger.warn("Failed to normalize priority value; passing through as given: %s", err instanceof Error ? err.message : String(err));
113
- return priority;
117
+ logger.warn("Failed to normalize default fields; passing through as given: %s", err instanceof Error ? err.message : String(err));
118
+ return { priority: fields.priority, case_type: fields.case_type };
114
119
  }
115
120
  }
116
121
  /**
@@ -183,8 +188,17 @@ async function toV1CustomFields(customFields, numericProjectId, config) {
183
188
  }
184
189
  export async function createTestCase(params, config) {
185
190
  const testCaseParams = { ...params };
186
- if (testCaseParams.priority !== undefined) {
187
- testCaseParams.priority = await normalizePriority(params.project_identifier, testCaseParams.priority, config);
191
+ testCaseParams.tags = Array.from(new Set([...(testCaseParams.tags ?? []), "MCP Generated"]));
192
+ if (testCaseParams.priority !== undefined ||
193
+ testCaseParams.case_type !== undefined) {
194
+ const normalized = await normalizeDefaultFields(params.project_identifier, {
195
+ priority: testCaseParams.priority,
196
+ case_type: testCaseParams.case_type,
197
+ }, config);
198
+ if (normalized.priority !== undefined)
199
+ testCaseParams.priority = normalized.priority;
200
+ if (normalized.case_type !== undefined)
201
+ testCaseParams.case_type = normalized.case_type;
188
202
  }
189
203
  const authString = getBrowserStackAuth(config);
190
204
  const [username, password] = authString.split(":");
@@ -1,7 +1,7 @@
1
1
  import { apiClient } from "../../lib/apiClient.js";
2
2
  import { z } from "zod";
3
3
  import { formatAxiosError } from "../../lib/error.js";
4
- import { fetchFormFields, normalizeDefaultFieldValue, projectIdentifierToId, } from "./TCG-utils/api.js";
4
+ import { fetchFormFields, normalizeDefaultFields as normalizeDefaultFieldsFromForm, projectIdentifierToId, } from "./TCG-utils/api.js";
5
5
  import { getTMBaseURL } from "../../lib/tm-base-url.js";
6
6
  import { getBrowserStackAuth } from "../../lib/get-auth.js";
7
7
  import logger from "../../logger.js";
@@ -82,26 +82,11 @@ async function normalizeDefaultFields(params, config) {
82
82
  try {
83
83
  const numericProjectId = await projectIdentifierToId(params.project_identifier, config);
84
84
  const { default_fields } = await fetchFormFields(numericProjectId, config);
85
- const out = {};
86
- if (params.priority !== undefined) {
87
- out.priority =
88
- normalizeDefaultFieldValue(default_fields?.priority?.values ?? [], params.priority, "name") ?? params.priority;
89
- }
90
- if (params.case_type !== undefined) {
91
- out.case_type =
92
- normalizeDefaultFieldValue(default_fields?.case_type?.values ?? [], params.case_type, "name") ?? params.case_type;
93
- }
94
- if (params.automation_status !== undefined) {
95
- // automation_status.values have null internal_name and the internal
96
- // name is actually held in `value` (see API inspection). Accept
97
- // either the display name or the internal snake_case form.
98
- const values = default_fields?.automation_status?.values ?? [];
99
- const input = params.automation_status.toLowerCase().trim();
100
- const match = values.find((v) => (v.value ?? "").toLowerCase() === input ||
101
- (v.name ?? "").toLowerCase() === input);
102
- out.automation_status = match?.value ?? params.automation_status;
103
- }
104
- return out;
85
+ return normalizeDefaultFieldsFromForm(default_fields, {
86
+ priority: params.priority,
87
+ case_type: params.case_type,
88
+ automation_status: params.automation_status,
89
+ });
105
90
  }
106
91
  catch (err) {
107
92
  logger.warn("Failed to normalize default field values; passing through as given: %s", err instanceof Error ? err.message : String(err));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserstack/mcp-server",
3
- "version": "1.2.29-beta.1",
3
+ "version": "1.2.30",
4
4
  "description": "BrowserStack's Official MCP Server",
5
5
  "mcpName": "io.github.browserstack/mcp-server",
6
6
  "main": "dist/index.js",