@smartbear/mcp 0.26.0 → 0.27.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.
Files changed (32) hide show
  1. package/README.md +10 -2
  2. package/dist/package.json.js +1 -1
  3. package/dist/qmetry/client/api/client-api.js +13 -1
  4. package/dist/qmetry/client/auto-resolve.js +23 -1
  5. package/dist/qmetry/client/handlers.js +10 -2
  6. package/dist/qmetry/client/issues.js +113 -1
  7. package/dist/qmetry/client/testcase.js +82 -1
  8. package/dist/qmetry/client/testsuite.js +20 -8
  9. package/dist/qmetry/client/tools/index.js +5 -2
  10. package/dist/qmetry/client/tools/issue-tools.js +163 -1
  11. package/dist/qmetry/client/tools/testcase-tools.js +81 -7
  12. package/dist/qmetry/client/tools/testsuite-tools.js +291 -26
  13. package/dist/qmetry/client/tools/udf-tools.js +294 -0
  14. package/dist/qmetry/client/udf.js +376 -0
  15. package/dist/qmetry/client.js +30 -3
  16. package/dist/qmetry/config/constants.js +63 -2
  17. package/dist/qmetry/config/rest-endpoints.js +7 -1
  18. package/dist/qmetry/types/common.js +85 -47
  19. package/dist/qmetry/types/issues.js +5 -0
  20. package/dist/qmetry/types/udf.js +77 -0
  21. package/dist/swagger/client/api.js +421 -8
  22. package/dist/swagger/client/configuration.js +9 -0
  23. package/dist/swagger/client/functional-testing-api.js +38 -2
  24. package/dist/swagger/client/functional-testing-tools.js +18 -0
  25. package/dist/swagger/client/functional-testing-types.js +11 -0
  26. package/dist/swagger/client/portal-types.js +35 -3
  27. package/dist/swagger/client/portal-utils.js +50 -0
  28. package/dist/swagger/client/registry-types.js +8 -0
  29. package/dist/swagger/client/tools.js +25 -4
  30. package/dist/swagger/client/utils.js +37 -0
  31. package/dist/swagger/client.js +37 -5
  32. package/package.json +1 -1
@@ -1,10 +1,10 @@
1
1
  import zod__default from "zod";
2
2
  import { getRequestHeader } from "../common/request-context.js";
3
- import { findAutoResolveConfig, autoResolveViewIdAndFolderPath } from "./client/auto-resolve.js";
3
+ import { extractProjectContext, findAutoResolveConfig, autoResolveViewIdAndFolderPath } from "./client/auto-resolve.js";
4
4
  import { QMETRY_HANDLER_MAP } from "./client/handlers.js";
5
5
  import { getProjectInfo } from "./client/project.js";
6
6
  import { TOOLS } from "./client/tools/index.js";
7
- import { QMETRY_DEFAULTS } from "./config/constants.js";
7
+ import { QMETRY_DEFAULTS, QMetryToolsHandlers } from "./config/constants.js";
8
8
  const ConfigurationSchema = zod__default.object({
9
9
  api_key: zod__default.string().describe("QMetry API key for authentication"),
10
10
  base_url: zod__default.string().url().optional().describe(
@@ -19,6 +19,8 @@ class QmetryClient {
19
19
  token;
20
20
  projectApiKey = QMETRY_DEFAULTS.PROJECT_KEY;
21
21
  endpoint = QMETRY_DEFAULTS.BASE_URL;
22
+ projectNumericId;
23
+ orgCode;
22
24
  async configure(_server, config, _cache) {
23
25
  this.token = config.api_key;
24
26
  if (config.base_url) {
@@ -42,6 +44,11 @@ class QmetryClient {
42
44
  getBaseUrl() {
43
45
  return this.endpoint;
44
46
  }
47
+ persistProjectContext(projectInfo) {
48
+ const { scopeId, orgCode } = extractProjectContext(projectInfo);
49
+ if (scopeId !== void 0) this.projectNumericId = scopeId;
50
+ if (orgCode !== void 0) this.orgCode = orgCode;
51
+ }
45
52
  async registerTools(register, _getInput) {
46
53
  const resolveContext = (args) => ({
47
54
  baseUrl: args.baseUrl ?? this.endpoint,
@@ -101,15 +108,35 @@ class QmetryClient {
101
108
  autoResolveConfig
102
109
  )
103
110
  );
111
+ this.persistProjectContext(projectInfo);
104
112
  }
105
113
  }
106
114
  const { projectKey: _, baseUrl: __, ...cleanArgs } = a;
115
+ const SCOPE_AWARE_HANDLERS = /* @__PURE__ */ new Set([
116
+ QMetryToolsHandlers.FETCH_TESTCASE_RUNS_BY_TESTSUITE_RUN,
117
+ QMetryToolsHandlers.BULK_UPDATE_TEST_RUN_UDFS,
118
+ QMetryToolsHandlers.FETCH_TEST_RUN_UDF_METADATA,
119
+ QMetryToolsHandlers.FETCH_TEST_RUN_UDF_VALUES,
120
+ QMetryToolsHandlers.FETCH_CASCADE_CHILD_VALUES
121
+ ]);
122
+ const isScopeAware = SCOPE_AWARE_HANDLERS.has(tool.handler);
123
+ const enrichedArgs = {
124
+ ...cleanArgs,
125
+ ...isScopeAware && this.projectNumericId !== void 0 && {
126
+ scopeId: this.projectNumericId
127
+ },
128
+ ...isScopeAware && this.orgCode !== void 0 && { orgCode: this.orgCode }
129
+ };
107
130
  const result = await handlerFn(
108
131
  this.getToken(),
109
132
  baseUrl,
110
133
  projectKey,
111
- cleanArgs
134
+ enrichedArgs
112
135
  );
136
+ if (tool.handler === QMetryToolsHandlers.SET_PROJECT_INFO) {
137
+ this.projectApiKey = projectKey;
138
+ this.persistProjectContext(result);
139
+ }
113
140
  const formatted = tool.formatResponse ? tool.formatResponse(result) : result ?? {};
114
141
  return {
115
142
  content: [
@@ -2,6 +2,58 @@ const QMETRY_DEFAULTS = {
2
2
  BASE_URL: "https://testmanagement.qmetry.com",
3
3
  PROJECT_KEY: "default"
4
4
  };
5
+ const UDF_MODULES = [
6
+ { id: 1, name: "Requirement" },
7
+ { id: 3, name: "Test Case" },
8
+ { id: 5, name: "Test Step" },
9
+ { id: 6, name: "Test Suite" },
10
+ { id: 11, name: "Issue" },
11
+ { id: 32, name: "Test Run" }
12
+ ];
13
+ const UDF_FIELD_TYPES = [
14
+ {
15
+ Id: 6,
16
+ Fieldtype: "STRING",
17
+ Description: "Any text string can be entered in this field in plain text.",
18
+ Preview: "ad-string-ico"
19
+ },
20
+ {
21
+ Id: 2,
22
+ Fieldtype: "LARGETEXT",
23
+ Description: "A large plain text field.",
24
+ Preview: "ad-large-text-ico"
25
+ },
26
+ {
27
+ Id: 3,
28
+ Fieldtype: "LOOKUPLIST",
29
+ Description: "An input which can be associated to any custom list define within the current project. Only one item can be selected at a time from this list.",
30
+ Preview: "ad-lookup-list-ico"
31
+ },
32
+ {
33
+ Id: 4,
34
+ Fieldtype: "MULTILOOKUPLIST",
35
+ Description: "An input which can be associated to any custom list define within the current project. Multiple is can be selected at a time.",
36
+ Preview: "ad-multi-select-lookup-list-ico"
37
+ },
38
+ {
39
+ Id: 5,
40
+ Fieldtype: "NUMBER",
41
+ Description: "Any number can be entered into this field.",
42
+ Preview: "ad-number-ico"
43
+ },
44
+ {
45
+ Id: 1,
46
+ Fieldtype: "DATETIMEPICKER",
47
+ Description: "This will open a calendar control which will allow the user to select a date.",
48
+ Preview: "ad-date-picker-ico"
49
+ },
50
+ {
51
+ Id: 7,
52
+ Fieldtype: "CASCADINGLIST",
53
+ Description: "A single-level cascading select list where child values depend on the parent selection.",
54
+ Preview: "ad-cascading-list-ico"
55
+ }
56
+ ];
5
57
  const QMetryToolsHandlers = {
6
58
  FETCH_PROJECTS: "getProjects",
7
59
  SET_PROJECT_INFO: "setProjectInfo",
@@ -42,9 +94,18 @@ const QMetryToolsHandlers = {
42
94
  FETCH_AUTOMATION_STATUS: "fetchAutomationStatus",
43
95
  CREATE_RELEASE: "createRelease",
44
96
  CREATE_CYCLE: "createCycle",
45
- UPDATE_CYCLE: "updateCycle"
97
+ UPDATE_CYCLE: "updateCycle",
98
+ FETCH_UDF_FIELD_TYPES: "fetchUdfFieldTypes",
99
+ FETCH_UDF_MODULES: "fetchUdfModules",
100
+ BULK_UPDATE_TEST_RUN_UDFS: "bulkUpdateTestRunUdfs",
101
+ FETCH_TEST_RUN_UDF_METADATA: "fetchTestRunUdfMetadata",
102
+ FETCH_TEST_RUN_UDF_VALUES: "fetchTestRunUdfValues",
103
+ FETCH_ISSUE_EXECUTIONS: "getIssueExecutions",
104
+ FETCH_CASCADE_CHILD_VALUES: "fetchCascadeChildValues"
46
105
  };
47
106
  export {
48
107
  QMETRY_DEFAULTS,
49
- QMetryToolsHandlers
108
+ QMetryToolsHandlers,
109
+ UDF_FIELD_TYPES,
110
+ UDF_MODULES
50
111
  };
@@ -40,11 +40,17 @@ const QMETRY_PATHS = {
40
40
  GET_ISSUES_LIST: "/rest/issues/list/viewColumns",
41
41
  CREATE_UPDATE_ISSUE: "/rest/issues",
42
42
  GET_ISSUES_LINKED_TO_TC: "/rest/issues/list/ForTC",
43
- LINK_ISSUES_TO_TESTCASE_RUN: "/rest/execution/link/issue"
43
+ LINK_ISSUES_TO_TESTCASE_RUN: "/rest/execution/link/issue",
44
+ GET_ISSUE_EXECUTIONS: "/rest/execution/getExecutionsForIssue"
44
45
  },
45
46
  AUTOMATION: {
46
47
  IMPORT_RESULTS: "/rest/import/createandscheduletestresults/1",
47
48
  GET_STATUS: "/rest/admin/status/automation/:requestID"
49
+ },
50
+ UDF: {
51
+ BULK_UPDATE_TEST_RUN_UDFS: "/rest/execution/udf/bulkupdate",
52
+ TEST_RUN_UDF_METADATA: "/rest/admin/udf/metadata",
53
+ FETCH_CASCADE_CHILD_VALUES: "/rest/admin/udf/cascade/childValue"
48
54
  }
49
55
  };
50
56
  export {
@@ -34,15 +34,15 @@ const SkipWarningEnum = z.enum(["0", "1"]);
34
34
  const CommonFields = {
35
35
  projectKey: z.string().describe("Project key - unique identifier for the project").default(QMETRY_DEFAULTS.PROJECT_KEY),
36
36
  projectKeyOptional: z.string().optional().describe("Project key - unique identifier for the project").default(QMETRY_DEFAULTS.PROJECT_KEY),
37
- baseUrl: z.string().url().optional().describe("The base URL for the QMetry instance (must be a valid URL)").default(QMETRY_DEFAULTS.BASE_URL),
37
+ baseUrl: z.string().url().optional().describe("The base URL for the QMetry instance (must be a valid URL)"),
38
38
  start: z.number().optional().describe("Start index for pagination - defaults to 0").default(0),
39
39
  page: z.number().optional().describe("Page number to return (starts from 1)").default(1),
40
40
  limit: z.number().optional().describe("Number of records (default 10).").default(10),
41
- tcID: z.number().describe(
42
- "Test Case numeric ID. This is the internal numeric identifier, not the entity key like 'MAC-TC-1684'. You can get this ID from test case search results or by using filters."
41
+ tcID: z.coerce.number().describe(
42
+ "Test Case numeric ID. CRITICAL: the parameter name is 'tcID' — do NOT use 'testCaseId', 'testCaseID', 'tcId', or other variants. Accepts a string or number. This is the internal numeric identifier, not the entity key like 'MAC-TC-1684'. You can get this ID from test case search results or by using filters."
43
43
  ),
44
- id: z.number().describe(
45
- "Test Case numeric ID (required for fetching steps or version details). This is the internal numeric identifier, not the entity key like 'MAC-TC-1684'. You can get this ID from test case search results."
44
+ id: z.coerce.number().describe(
45
+ "Test Case numeric ID (required for fetching steps or version details). Accepts a string or number. This is the internal numeric identifier, not the entity key like 'MAC-TC-1684'. You can get this ID from test case search results."
46
46
  ),
47
47
  version: z.number().describe(
48
48
  "Test Case version number. This is the internal numeric identifier for the version."
@@ -59,10 +59,10 @@ const CommonFields = {
59
59
  rqVersion: z.number().describe(
60
60
  "Requirement version number (required for fetching specific requirement version details). This is the internal numeric identifier for the version."
61
61
  ),
62
- tcViewId: z.number().describe(
62
+ tcViewId: z.number().optional().describe(
63
63
  "ViewId for test cases - SYSTEM AUTOMATICALLY RESOLVES THIS. Leave empty unless you have a specific viewId. System will fetch project info using the projectKey and extract latestViews.TC.viewId automatically. Manual viewId only needed if you want to override the automatic resolution."
64
64
  ),
65
- rqViewId: z.number().describe(
65
+ rqViewId: z.number().optional().describe(
66
66
  "ViewId for requirements - SYSTEM AUTOMATICALLY RESOLVES THIS. Leave empty unless you have a specific viewId. System will fetch project info using the projectKey and extract latestViews.RQ.viewId automatically. Manual viewId only needed if you want to override the automatic resolution."
67
67
  ),
68
68
  rqFolderPath: z.string().optional().describe(
@@ -77,28 +77,28 @@ const CommonFields = {
77
77
  folderID: z.number().optional().describe(
78
78
  "Folder ID - unique numeric identifier for the specific folder. Use this to target a specific folder within the project hierarchy. Applies to any entity type (test cases, requirements, test suites, etc.)."
79
79
  ),
80
- tsFolderID: z.number().describe(
81
- "Test Suite folder ID (required for fetching test suites). This is the numeric identifier for the test suite folder. IMPORTANT: Get from project info response → rootFolders.TS.id (e.g., 113557 for MAC project). Use FETCH_PROJECT_INFO tool first to get this ID if not provided by user. For root folder: use rootFolders.TS.id, for sub-folders: use specific folder IDs."
80
+ tsFolderID: z.coerce.number().describe(
81
+ "Test Suite folder ID. CRITICAL: the parameter name is 'tsFolderID' do NOT use 'testSuiteFolderId', 'folderId', 'folderID', or other variants. Accepts a string or number. Get from project info response → rootFolders.TS.id. Use FETCH_PROJECT_INFO tool first if not provided by user."
82
82
  ),
83
- tsID: z.number().describe(
84
- "Test Suite numeric ID (required for fetching test cases linked to test suite). This is the internal numeric identifier, not the entity key. NOTE: To get the tsID - Call API 'Testsuite/Fetch Testsuite' From the response, get value of following attribute -> data[<index>].id"
83
+ tsID: z.coerce.number().describe(
84
+ "Test Suite numeric ID. CRITICAL: the parameter name is 'tsID' do NOT use 'testSuiteId', 'testSuiteID', 'suiteId', or other variants. Accepts a string or number. NOTE: To get the tsID - Call API 'Testsuite/Fetch Testsuite' From the response, get value of following attribute -> data[<index>].id"
85
85
  ),
86
86
  gridName: z.string().optional().describe("Grid Name to be displayed (default 'TESTEXECUTIONLIST')"),
87
87
  teViewId: z.number().optional().describe(
88
88
  "ViewId for test execution - SYSTEM AUTOMATICALLY RESOLVES THIS. Leave empty unless you have a specific viewId. System will fetch project info using the projectKey and extract latestViews.TE.viewId automatically. Manual viewId only needed if you want to override the automatic resolution."
89
89
  ),
90
- tsfeViewId: z.number().describe(
90
+ tsfeViewId: z.number().optional().describe(
91
91
  "ViewId for test suite folders - SYSTEM AUTOMATICALLY RESOLVES THIS. Leave empty unless you have a specific viewId. System will fetch project info using the projectKey and extract latestViews.TSFS.viewId automatically. Manual viewId only needed if you want to override the automatic resolution."
92
92
  ),
93
- tsViewId: z.number().describe(
93
+ tsViewId: z.number().optional().describe(
94
94
  "ViewId for test suites - SYSTEM AUTOMATICALLY RESOLVES THIS. Leave empty unless you have a specific viewId. System will fetch project info using the projectKey and extract latestViews.TS.viewId automatically. Manual viewId only needed if you want to override the automatic resolution."
95
95
  ),
96
- tsrunID: z.string().describe(
97
- "Test Suite Run ID (required for fetching test case runs). This is the string identifier for the test suite run execution. NOTE: To get the tsrunID - Call API 'Execution/Fetch Executions' From the response, get value of following attribute -> data[<index>].tsRunID"
96
+ tsrunID: z.coerce.string().describe(
97
+ "Test Suite Run ID. CRITICAL: the parameter name is 'tsrunID' do NOT use 'testSuiteRunId', 'tsRunID', 'testSuiteRunID', or any other variant. Accepts a string or number (e.g. 731600 or '731600' both are valid). To get this value: Call 'Fetch Executions by Test Suite' → use data[<index>].tsRunID from the response."
98
98
  ),
99
99
  showTcWithDefects: z.boolean().optional().describe("Show test case runs with linked defects").default(false),
100
- entityId: z.number().describe(
101
- "Id of Test case run (required for fetching linked issues). This is the internal numeric identifier for the test case run execution. NOTE: To get the entityId - Call API 'Execution/Fetch Testcase Run ID' From the response, get value of following attribute -> data[<index>].tcRunID"
100
+ entityId: z.coerce.number().describe(
101
+ "Id of Test case run (required for fetching linked issues). CRITICAL: the parameter name is 'entityId' do NOT use 'tcRunId', 'testCaseRunId', 'runId', or other variants. Accepts a string or number. NOTE: To get the entityId - Call API 'Execution/Fetch Testcase Run ID' From the response, get value of following attribute -> data[<index>].tcRunID"
102
102
  ),
103
103
  getLinked: z.boolean().optional().describe(
104
104
  "True to get only those issues that are linked with this Test case Run, False to get those issues which are not linked with this Test case Run. Default value true (get linked issues)."
@@ -109,6 +109,9 @@ const CommonFields = {
109
109
  ).default("project"),
110
110
  filter: z.string().optional().describe("Filter criteria as JSON string (default '[]')").default("[]"),
111
111
  udfFilter: z.string().optional().describe("User-defined field filter as JSON string (default '[]')").default("[]"),
112
+ tcrUdfFilter: z.string().optional().describe(
113
+ `Test Case Run (TCR) UDF filter as JSON string (default '[]'). Used to filter test case runs by Test Run UDF field values. Format: '[{"type":"list","value":[<listItemId1>,<listItemId2>],"field":"<udfFieldKey>"}]'. Example: '[{"type":"list","value":[5108701,5108697],"field":"8260LUP"}]' filters runs where the UDF field '8260LUP' has those list item IDs.`
114
+ ).default("[]"),
112
115
  showRootOnly: z.boolean().optional().describe("Whether to show only root folders."),
113
116
  getSubEntities: z.boolean().optional().describe("Whether to include sub-entities."),
114
117
  getColumns: z.boolean().optional().describe("Whether to get column information in response.").default(true),
@@ -242,7 +245,9 @@ const UpdateTestCaseRemoveStepSchema = z.object({
242
245
  tcsIsParameterized: z.boolean()
243
246
  });
244
247
  const CreateTestCaseArgsSchema = z.object({
245
- tcFolderID: z.string(),
248
+ tcFolderID: z.string().optional().describe(
249
+ "Test Case folder ID - SYSTEM AUTOMATICALLY RESOLVES THIS. Leave empty unless you have a specific folder ID. System will fetch project info using the projectKey and extract rootFolders.TC.id automatically. Manual folder ID only needed if you want to target a specific sub-folder."
250
+ ),
246
251
  steps: z.array(CreateTestCaseStepSchema).optional(),
247
252
  name: z.string(),
248
253
  priority: z.number().optional(),
@@ -420,18 +425,20 @@ const TestCasesLinkedToRequirementArgsSchema = z.object({
420
425
  tcFolderPath: z.string().optional().describe(
421
426
  'Folder path to get test cases under specific folder. Use empty string "" for root folder or specify path like "/Sample Template".'
422
427
  ).default(""),
423
- releaseID: z.string().optional().describe(
424
- "Filter test cases by release ID. Use string representation of release ID (e.g., '7138'). Get release IDs from FETCH_RELEASES_AND_CYCLES tool."
428
+ releaseID: z.coerce.string().optional().describe(
429
+ "Filter test cases by release ID. Accepts a string or number. Get release IDs from FETCH_RELEASES_AND_CYCLES tool."
425
430
  ),
426
- cycleID: z.string().optional().describe(
427
- "Filter test cases by cycle ID. Use string representation of cycle ID (e.g., '13382'). Get cycle IDs from FETCH_RELEASES_AND_CYCLES tool."
431
+ cycleID: z.coerce.string().optional().describe(
432
+ "Filter test cases by cycle ID. Accepts a string or number. Get cycle IDs from FETCH_RELEASES_AND_CYCLES tool."
428
433
  ),
429
434
  filter: CommonFields.filter,
430
435
  getSubEntities: z.boolean().optional().describe("Allow filter of sub-entities for requirement.").default(true),
431
436
  getColumns: z.boolean().optional().describe("True to get column information in response.").default(true)
432
437
  });
433
438
  const CreateTestSuiteArgsSchema = z.object({
434
- parentFolderId: z.string(),
439
+ parentFolderId: z.string().optional().describe(
440
+ "Test Suite parent folder ID - SYSTEM AUTOMATICALLY RESOLVES THIS. Leave empty unless you have a specific folder ID. System will fetch project info using the projectKey and extract rootFolders.TS.id automatically. Manual folder ID only needed if you want to target a specific sub-folder."
441
+ ),
435
442
  name: z.string(),
436
443
  isAutomatedFlag: z.boolean().optional(),
437
444
  description: z.string().optional(),
@@ -447,7 +454,9 @@ const CreateTestSuiteArgsSchema = z.object({
447
454
  });
448
455
  const UpdateTestSuiteArgsSchema = z.object({
449
456
  id: z.number().describe("Id of Test Suite to be updated (required)"),
450
- TsFolderID: z.number().describe("Folder ID where Test Suite resides (required)"),
457
+ TsFolderID: z.number().optional().describe(
458
+ "Folder ID where Test Suite resides - SYSTEM AUTOMATICALLY RESOLVES THIS. Leave empty unless you have a specific folder ID. System will fetch project info using the projectKey and extract rootFolders.TS.id automatically. Manual folder ID only needed if you want to override the automatic resolution."
459
+ ),
451
460
  entityKey: z.string().describe("Entity Key of Test Suite to be updated (required)"),
452
461
  name: z.string().optional().describe("Name of the Test Suite"),
453
462
  description: z.string().optional().describe("Description of the Test Suite"),
@@ -473,8 +482,8 @@ const TestSuiteListArgsSchema = z.object({
473
482
  const TestSuitesForTestCaseArgsSchema = z.object({
474
483
  projectKey: CommonFields.projectKeyOptional,
475
484
  baseUrl: CommonFields.baseUrl,
476
- tsFolderID: CommonFields.tsFolderID,
477
- viewId: CommonFields.tsfeViewId.optional(),
485
+ tsFolderID: CommonFields.tsFolderID.optional(),
486
+ viewId: CommonFields.tsfeViewId,
478
487
  start: CommonFields.start,
479
488
  page: CommonFields.page,
480
489
  limit: CommonFields.limit,
@@ -482,16 +491,20 @@ const TestSuitesForTestCaseArgsSchema = z.object({
482
491
  filter: CommonFields.filter
483
492
  });
484
493
  const LinkTestCasesToTestSuiteArgsSchema = z.object({
485
- tsID: z.number().describe("Id of Test Suite (required)"),
486
- tcvdIDs: z.array(z.number()).describe(
487
- "Array of Test Case Version IDs (required if fromReqs is false)"
494
+ tsID: z.coerce.number().describe(
495
+ "Id of Test Suite (required). CRITICAL: parameter name is 'tsID' — do NOT use 'testSuiteId', 'testSuiteID', or other variants."
496
+ ),
497
+ tcvdIDs: z.array(z.coerce.number()).describe(
498
+ "Array of Test Case Version IDs (required if fromReqs is false). CRITICAL: parameter name is 'tcvdIDs' — do NOT use 'tcVersionIds', 'testCaseVersionIds', or other variants."
488
499
  ),
489
500
  fromReqs: z.boolean().optional().describe("Link TestCases from Requirements (optional, default false)")
490
501
  }).strip();
491
502
  const RequirementsLinkedTestCasesToTestSuiteArgsSchema = z.object({
492
- tsID: z.number().describe("Id of Test Suite (required)"),
493
- tcvdIDs: z.array(z.number()).describe(
494
- "Array of Test Case Version IDs (required if fromReqs is true)"
503
+ tsID: z.coerce.number().describe(
504
+ "Id of Test Suite (required). CRITICAL: parameter name is 'tsID' — do NOT use 'testSuiteId', 'testSuiteID', or other variants."
505
+ ),
506
+ tcvdIDs: z.array(z.coerce.number()).describe(
507
+ "Array of Test Case Version IDs (required if fromReqs is true). CRITICAL: parameter name is 'tcvdIDs' — do NOT use 'tcVersionIds', 'testCaseVersionIds', or other variants."
495
508
  ),
496
509
  fromReqs: z.boolean().optional().describe("Link TestCases from Requirements (optional, default true)")
497
510
  }).strip();
@@ -533,11 +546,15 @@ const TestCaseRunsByTestSuiteRunArgsSchema = z.object({
533
546
  baseUrl: CommonFields.baseUrl,
534
547
  tsrunID: CommonFields.tsrunID,
535
548
  // API payload param - sent in request body (REQUIRED)
536
- viewId: CommonFields.teViewId.pipe(z.number()),
537
- // API payload param - sent in request body (REQUIRED)
549
+ viewId: CommonFields.teViewId,
550
+ // auto-resolved via SYSTEM if not provided
538
551
  start: CommonFields.start,
539
552
  page: CommonFields.page,
540
- limit: CommonFields.limit
553
+ limit: CommonFields.limit,
554
+ filter: CommonFields.filter,
555
+ udfFilter: CommonFields.udfFilter,
556
+ tcrUdfFilter: CommonFields.tcrUdfFilter,
557
+ showTcWithDefects: CommonFields.showTcWithDefects
541
558
  });
542
559
  const LinkedIssuesByTestCaseRunArgsSchema = z.object({
543
560
  projectKey: CommonFields.projectKeyOptional,
@@ -569,7 +586,9 @@ const CreateIssueArgsSchema = z.object({
569
586
  )
570
587
  });
571
588
  const UpdateIssueArgsSchema = z.object({
572
- DefectId: z.number().describe("ID of the defect/issue to be updated"),
589
+ DefectId: z.coerce.number().describe(
590
+ "ID of the defect/issue to be updated. CRITICAL: the parameter name is 'DefectId' (capital D) — do NOT use 'defectId', 'issueId', 'id', or other variants. Accepts a string or number."
591
+ ),
573
592
  entityKey: z.string().optional().describe("Entity Key of the defect/issue to be updated"),
574
593
  issueType: z.number().optional().describe("Issue type ID (e.g. Bug, Enhancement, etc.)"),
575
594
  issuePriority: z.number().optional().describe("Issue priority ID (e.g. High, Medium, Low, etc.)"),
@@ -582,7 +601,7 @@ const UpdateIssueArgsSchema = z.object({
582
601
  const IssuesListArgsSchema = z.object({
583
602
  projectKey: CommonFields.projectKeyOptional,
584
603
  baseUrl: CommonFields.baseUrl,
585
- viewId: z.number().describe(
604
+ viewId: z.number().optional().describe(
586
605
  "ViewId for issues - SYSTEM AUTOMATICALLY RESOLVES THIS. Leave empty unless you have a specific viewId. System will fetch project info using the projectKey and extract latestViews.IS.viewId automatically. Manual viewId only needed if you want to override the automatic resolution."
587
606
  ),
588
607
  start: CommonFields.start,
@@ -598,29 +617,45 @@ const LinkIssuesToTestcaseRunArgsSchema = z.object({
598
617
  projectKey: CommonFields.projectKeyOptional,
599
618
  baseUrl: CommonFields.baseUrl,
600
619
  issueIds: z.array(z.union([z.string(), z.number()])).describe("ID of issues to be linked to Testcase Run"),
601
- tcrId: z.number().describe("ID of Testcase Run to link issues with")
620
+ tcrId: z.coerce.number().describe(
621
+ "ID of Testcase Run to link issues with. CRITICAL: parameter name is 'tcrId' — do NOT use 'tcRunId', 'testCaseRunId', or other variants. Accepts a string or number."
622
+ )
623
+ });
624
+ const IssueExecutionsArgsSchema = z.object({
625
+ projectKey: CommonFields.projectKeyOptional,
626
+ baseUrl: CommonFields.baseUrl,
627
+ linkedAssetId: z.coerce.number().describe(
628
+ "Numeric defect ID of the QMetry issue. CRITICAL: parameter name is 'linkedAssetId' — do NOT use 'issueId', 'defectId', 'id', or other variants. Accepts a string or number. To get this ID, call the Fetch Defects or Issues tool and use data[<index>].id from the response."
629
+ ),
630
+ start: CommonFields.start,
631
+ page: CommonFields.page,
632
+ limit: CommonFields.limit,
633
+ platformID: z.string().optional().describe("Platform ID to filter executions by environment/platform"),
634
+ filter: z.string().optional().describe(
635
+ `JSON filter string. Supported fields: tcName (string), linkageLevel (string), executedVersion (string), runStatusName (list of status names), platformID (list of numeric IDs), executionCreatedByLoginAlias (list of usernames), isTestSuiteArchived (list: [1] active, [0] archived, [1,0] both). Example: '[{"type":"string","value":"login","field":"tcName"}]'`
636
+ ).default("[]")
602
637
  });
603
638
  const LinkPlatformsToTestSuiteArgsSchema = z.object({
604
639
  projectKey: CommonFields.projectKeyOptional,
605
640
  baseUrl: CommonFields.baseUrl,
606
- qmTsId: z.number().describe(
607
- "Id of Test Suite (required). To get the qmTsId - Call API 'Testsuite/Fetch Testsuite' From the response, get value of following attribute -> data[<index>].id"
641
+ qmTsId: z.coerce.number().describe(
642
+ "Id of Test Suite (required). CRITICAL: the parameter name is 'qmTsId' — do NOT use 'tsId', 'testSuiteId', 'tsID', or other variants. Accepts a string or number. To get the qmTsId - Call API 'Testsuite/Fetch Testsuite'. From the response, get value -> data[<index>].id"
608
643
  ),
609
- qmPlatformId: z.string().describe(
610
- "Comma-separated value of PlatformId (required). To get the qmPlatformId - Call API 'Platform/List' From the response, get value of following attribute -> data[<index>].platformID"
644
+ qmPlatformId: z.coerce.string().describe(
645
+ "Comma-separated Platform IDs (required). CRITICAL: the parameter name is 'qmPlatformId' — do NOT use 'platformId', 'platformID', 'platformIds', or other variants. Accepts a number or string. To get the qmPlatformId - Call API 'Platform/List'. From the response, get value -> data[<index>].platformID"
611
646
  )
612
647
  });
613
648
  const BulkUpdateExecutionStatusArgsSchema = z.object({
614
649
  projectKey: CommonFields.projectKeyOptional,
615
650
  baseUrl: CommonFields.baseUrl,
616
- entityIDs: z.string().describe(
617
- "Comma-separated IDs of Test Case Runs to update (e.g., '66095087' for single or '66095069,66095075' for bulk). To get the entityIDs - Call API 'Execution/Fetch Testcase Run ID' From the response, get value of following attribute -> data[<index>].tcRunID"
651
+ entityIDs: z.coerce.string().describe(
652
+ "Comma-separated IDs of Test Case Runs to update. CRITICAL: the parameter name is 'entityIDs' — do NOT use 'tcRunIDs', 'testCaseRunIds', 'runIds', or other variants. Accepts a number or string (e.g., 66095087 or '66095087' for single, '66095069,66095075' for bulk). To get the entityIDs - Call API 'Execution/Fetch Testcase Run ID'. From the response, get value -> data[<index>].tcRunID"
618
653
  ),
619
654
  entityType: z.enum(["TCR", "TCSR"]).describe(
620
655
  "Type of Entity to Execute: 'TCR' (Test Case Run) or 'TCSR' (Test Case Step Run)"
621
656
  ).default("TCR"),
622
- qmTsRunId: z.string().describe(
623
- "Id of Test Suite Run to execute (required). To get the qmTsRunId - Call API 'Execution/Fetch Executions' From the response, get value of following attribute -> data[<index>].tsRunID"
657
+ qmTsRunId: z.coerce.string().describe(
658
+ "Id of Test Suite Run to execute (required). CRITICAL: the parameter name is 'qmTsRunId' — do NOT use 'tsrunID', 'testSuiteRunId', 'tsRunID', or other variants. Accepts a number or string. To get the qmTsRunId - Call API 'Execution/Fetch Executions'. From the response, get value -> data[<index>].tsRunID"
624
659
  ),
625
660
  runStatusID: z.number().describe(
626
661
  "Id of the execution status to set (required). To get the runStatusID - Call API 'Admin/Project GET info Service' From the response, get value of following attribute -> allstatus[<index>].id Common statuses: Pass, Fail, Not Run, Blocked, WIP, etc."
@@ -718,7 +753,9 @@ const ImportAutomationResultsPayloadSchema = z.object({
718
753
  const FetchAutomationStatusPayloadSchema = z.object({
719
754
  projectKey: CommonFields.projectKeyOptional,
720
755
  baseUrl: CommonFields.baseUrl,
721
- requestID: z.number().describe("Numeric request ID from import automation response")
756
+ requestID: z.coerce.number().describe(
757
+ "Numeric request ID from import automation response. CRITICAL: parameter name is 'requestID' — do NOT use 'requestId', 'jobId', or other variants. Accepts a string or number."
758
+ )
722
759
  });
723
760
  export {
724
761
  AutomationHierarchyEnum,
@@ -739,6 +776,7 @@ export {
739
776
  ExecutionsByTestSuiteArgsSchema,
740
777
  FetchAutomationStatusPayloadSchema,
741
778
  ImportAutomationResultsPayloadSchema,
779
+ IssueExecutionsArgsSchema,
742
780
  IssuesLinkedToTestCaseArgsSchema,
743
781
  IssuesListArgsSchema,
744
782
  LinkIssuesToTestcaseRunArgsSchema,
@@ -7,6 +7,10 @@ const DEFAULT_FETCH_ISSUES_LINKED_TO_TESTCASE_PAYLOAD = {
7
7
  const DEFAULT_CREATE_ISSUE_PAYLOAD = {};
8
8
  const DEFAULT_UPDATE_ISSUE_PAYLOAD = {};
9
9
  const DEFAULT_LINK_ISSUES_TO_TESTCASE_RUN_PAYLOAD = {};
10
+ const DEFAULT_FETCH_ISSUE_EXECUTIONS_PAYLOAD = {
11
+ ...DEFAULT_PAGINATION,
12
+ filter: "[]"
13
+ };
10
14
  const DEFAULT_FETCH_ISSUES_PAYLOAD = {
11
15
  ...DEFAULT_PAGINATION,
12
16
  ...DEFAULT_FILTER,
@@ -23,6 +27,7 @@ export {
23
27
  DEFAULT_CREATE_ISSUE_PAYLOAD,
24
28
  DEFAULT_FETCH_ISSUES_LINKED_TO_TESTCASE_PAYLOAD,
25
29
  DEFAULT_FETCH_ISSUES_PAYLOAD,
30
+ DEFAULT_FETCH_ISSUE_EXECUTIONS_PAYLOAD,
26
31
  DEFAULT_LINK_ISSUES_TO_TESTCASE_RUN_PAYLOAD,
27
32
  DEFAULT_UPDATE_ISSUE_PAYLOAD
28
33
  };
@@ -0,0 +1,77 @@
1
+ import { z } from "zod";
2
+ import { CommonFields } from "./common.js";
3
+ z.object({
4
+ projectKey: CommonFields.projectKey,
5
+ baseUrl: CommonFields.baseUrl
6
+ });
7
+ z.object({
8
+ projectKey: CommonFields.projectKeyOptional,
9
+ baseUrl: CommonFields.baseUrl
10
+ });
11
+ const UdfFieldValueSchema = z.object({
12
+ fieldID: z.number().int().positive().describe(
13
+ "Numeric ID of the UDF field to update. Get this from the UDF field definition in QMetry admin or from the field's metadata."
14
+ ),
15
+ value: z.union([
16
+ z.string(),
17
+ z.number(),
18
+ z.array(z.number().int()),
19
+ z.object({
20
+ parent: z.number().int().describe("Parent item ID for cascading list."),
21
+ child: z.number().int().describe("Child item ID for cascading list.")
22
+ })
23
+ ]).describe(
24
+ "Value to set for this UDF field. Type depends on field type:\nSTRING: plain string, e.g. 'test value'\nNUMBER: number, e.g. 3\nDATETIMEPICKER: date string in MM-DD-YYYY format, e.g. '06-20-2026'\nLOOKUPLIST (single): numeric item ID, e.g. 5108697\nMULTILOOKUPLIST (multi-select): array of item IDs, e.g. [5158524, 5158525]\nCASCADINGLIST: object with parent and child IDs, e.g. {parent: 5126498, child: 5126499}"
25
+ ),
26
+ multiSelectAction: z.enum(["append", "replace"]).optional().default("append").describe(
27
+ "Action for MULTILOOKUPLIST fields only. 'append' adds the new values to existing selections (default). 'replace' clears existing selections and sets only the new values. Omit or use 'append' if user does not specify — never assume 'replace'."
28
+ )
29
+ });
30
+ const BulkUpdateTestRunUdfsArgsSchema = z.object({
31
+ projectKey: CommonFields.projectKeyOptional,
32
+ baseUrl: CommonFields.baseUrl,
33
+ tcRunIDs: z.array(z.number().int().positive()).min(1).describe(
34
+ "Array of Test Case Run IDs to update UDF values for. To get tcRunIDs — Call 'Fetch Test Case Runs by Test Suite Run' tool. From the response, get value of data[<index>].tcRunID. Example: [41572006, 41572009, 41572013]"
35
+ ),
36
+ UDF: z.record(z.string(), UdfFieldValueSchema).describe(
37
+ "Object mapping UDF field names to their new values. Each key is the UDF field name (e.g. 'test_env', 'priority_field'). Each value is an object with fieldID and value (and optionally multiSelectAction for multi-select fields). All UDF fields are optional — include only the fields you want to update."
38
+ )
39
+ });
40
+ const FetchTestRunUdfMetadataArgsSchema = z.object({
41
+ projectKey: CommonFields.projectKeyOptional,
42
+ baseUrl: CommonFields.baseUrl
43
+ });
44
+ const FetchCascadeChildValuesArgsSchema = z.object({
45
+ projectKey: CommonFields.projectKeyOptional,
46
+ baseUrl: CommonFields.baseUrl,
47
+ id: z.number().int().positive().describe(
48
+ "Numeric ID of the parent cascade list item to fetch child values for. Get this from the 'lookupOptions' returned by 'Fetch Test Run UDF Metadata' for a CASCADINGLIST field — each option has an 'id' field."
49
+ ),
50
+ isArchReq: z.boolean().optional().default(false).describe(
51
+ "Whether to include archived child items in the response (default: false)."
52
+ )
53
+ });
54
+ const FetchTestRunUdfValuesArgsSchema = z.object({
55
+ projectKey: CommonFields.projectKeyOptional,
56
+ baseUrl: CommonFields.baseUrl,
57
+ tsrunID: z.coerce.string().optional().describe(
58
+ "Test Suite Run ID. CRITICAL: the parameter name is 'tsrunID' — do NOT use 'testSuiteRunId', 'tsRunID', or any other variant. Accepts a string or number (e.g. 731600 or '731600' — both are valid). Get this from 'Fetch Executions by Test Suite' → use data[<index>].tsRunID from the response. Required when sourceRows is not provided."
59
+ ),
60
+ viewId: z.number().int().positive().optional().describe(
61
+ "View ID for the test execution list (latestViews.TE.viewId from project info). Auto-resolved from project info when omitted. Required when sourceRows is not provided."
62
+ ),
63
+ sourceContext: z.enum(["testSuiteRun", "testCaseExecutions"]).optional().default("testSuiteRun").describe(
64
+ "Which parent tool produced sourceRows. Use 'testSuiteRun' for Fetch Test Case Runs by Test Suite Run. Do NOT use this tool for Fetch Test Case Executions — that tool calls metadata internally and returns testRunUdfs on every execution row; use that data directly. Do NOT use this tool for Fetch Issue Executions; that tool already reads udfjson and enriches it with metadata."
65
+ ),
66
+ sourceRows: z.array(z.record(z.string(), z.unknown())).optional().describe(
67
+ "Optional rows already returned by Fetch Test Case Runs by Test Suite Run. The UDF tool will reuse these rows, enrich/pivot UDF values, and preserve identification fields instead of making the same execution-list API call again. Do NOT pass Fetch Test Case Executions rows here — those rows already have testRunUdfs enriched. Do not pass issue execution rows here; use Fetch Issue Executions output directly for issue UDFs."
68
+ ),
69
+ startIndex: z.number().int().min(0).optional().default(0).describe("Zero-based start index for pagination (default: 0)."),
70
+ size: z.number().int().min(1).optional().default(50).describe("Number of test case runs to return per page (default: 50).")
71
+ });
72
+ export {
73
+ BulkUpdateTestRunUdfsArgsSchema,
74
+ FetchCascadeChildValuesArgsSchema,
75
+ FetchTestRunUdfMetadataArgsSchema,
76
+ FetchTestRunUdfValuesArgsSchema
77
+ };