@browserstack/mcp-server 1.2.31 → 1.2.32

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.
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { formatAxiosError } from "../../lib/error.js";
4
4
  import { fetchFormFields, normalizeDefaultFields as normalizeDefaultFieldsFromForm, projectIdentifierToId, } from "./TCG-utils/api.js";
5
5
  import { getTMBaseURL } from "../../lib/tm-base-url.js";
6
+ import { wrapRichText, wrapTestCaseSteps } from "./rich-text.js";
6
7
  import logger from "../../logger.js";
7
8
  // Scalar value, or an array of values for multi-select custom fields.
8
9
  export const customFieldValueSchema = z.union([
@@ -189,6 +190,13 @@ async function toV1CustomFields(customFields, numericProjectId, config) {
189
190
  export async function createTestCase(params, config) {
190
191
  const testCaseParams = { ...params };
191
192
  testCaseParams.tags = Array.from(new Set([...(testCaseParams.tags ?? []), "MCP Generated"]));
193
+ testCaseParams.test_case_steps = wrapTestCaseSteps(testCaseParams.test_case_steps);
194
+ if (testCaseParams.preconditions !== undefined) {
195
+ testCaseParams.preconditions = wrapRichText(testCaseParams.preconditions);
196
+ }
197
+ if (testCaseParams.description !== undefined) {
198
+ testCaseParams.description = wrapRichText(testCaseParams.description);
199
+ }
192
200
  if (testCaseParams.priority !== undefined ||
193
201
  testCaseParams.case_type !== undefined) {
194
202
  const normalized = await normalizeDefaultFields(params.project_identifier, {
@@ -0,0 +1,5 @@
1
+ export declare function wrapRichText(text: string): string;
2
+ export declare function wrapTestCaseSteps<T extends {
3
+ step: string;
4
+ result: string;
5
+ }>(steps: T[]): T[];
@@ -0,0 +1,22 @@
1
+ // TM renders these fields as rich text only when the value is HTML;
2
+ const TM_ALLOWED_TAG = /^\s*<(div|img|b|em|i|strong|u|span|abbr|br|cite|code|dd|dfn|dl|dt|kbd|li|mark|ol|p|pre|q|s|samp|small|strike|sub|sup|time|ul|var|table|td|th|thead|tbody|tr|col|colgroup|a)[\s/>]/i;
3
+ const HAS_ENCODABLE_CHARS = /[<>&]/;
4
+ function escapeHtml(text) {
5
+ return text
6
+ .replace(/&/g, "&amp;")
7
+ .replace(/</g, "&lt;")
8
+ .replace(/>/g, "&gt;");
9
+ }
10
+ export function wrapRichText(text) {
11
+ if (TM_ALLOWED_TAG.test(text) || !HAS_ENCODABLE_CHARS.test(text)) {
12
+ return text;
13
+ }
14
+ return `<p>${escapeHtml(text)}</p>`;
15
+ }
16
+ export function wrapTestCaseSteps(steps) {
17
+ return steps.map((s) => ({
18
+ ...s,
19
+ step: wrapRichText(s.step),
20
+ result: wrapRichText(s.result),
21
+ }));
22
+ }
@@ -4,6 +4,7 @@ import { formatAxiosError } from "../../lib/error.js";
4
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
+ import { wrapRichText, wrapTestCaseSteps } from "./rich-text.js";
7
8
  import logger from "../../logger.js";
8
9
  export const UpdateTestCaseSchema = z.object({
9
10
  project_identifier: z
@@ -107,11 +108,11 @@ export async function updateTestCase(params, config) {
107
108
  if (params.name !== undefined)
108
109
  testCaseBody.name = params.name;
109
110
  if (params.description !== undefined)
110
- testCaseBody.description = params.description;
111
+ testCaseBody.description = wrapRichText(params.description);
111
112
  if (params.preconditions !== undefined)
112
- testCaseBody.preconditions = params.preconditions;
113
+ testCaseBody.preconditions = wrapRichText(params.preconditions);
113
114
  if (params.test_case_steps !== undefined)
114
- testCaseBody.test_case_steps = params.test_case_steps;
115
+ testCaseBody.test_case_steps = wrapTestCaseSteps(params.test_case_steps);
115
116
  if (params.owner !== undefined)
116
117
  testCaseBody.owner = params.owner;
117
118
  if (params.status !== undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserstack/mcp-server",
3
- "version": "1.2.31",
3
+ "version": "1.2.32",
4
4
  "description": "BrowserStack's Official MCP Server",
5
5
  "mcpName": "io.github.browserstack/mcp-server",
6
6
  "main": "dist/index.js",