UNPKG

@smartbear/mcp

Version:

MCP server for interacting SmartBear Products

1,060 lines (1,059 loc) 43.9 kB
import { z } from "zod"; import { QMETRY_DEFAULTS } from "../config/constants.js"; export const DEFAULT_PAGINATION = { start: 0, page: 1, limit: 10, }; export const DEFAULT_FILTER = { filter: "[]", }; export const DEFAULT_SORT = { sort: '[{"property":"name","direction":"ASC"}]', }; export const DEFAULT_FOLDER_OPTIONS = { scope: "project", showRootOnly: false, getSubEntities: true, hideEmptyFolders: false, folderSortColumn: "name", folderSortOrder: "ASC", restoreDefaultColumns: false, folderID: null, }; /** * Entity types supported for automation result import */ export const EntityTypeEnum = z.enum([ "TESTNG", "CUCUMBER", "JUNIT", "HPUFT", "QAF", "ROBOT", ]); /** * Automation hierarchy options for TestNG and JUnit */ export const AutomationHierarchyEnum = z.enum(["1", "2", "3"]); /** * Skip warning options */ export const SkipWarningEnum = z.enum(["0", "1"]); // Reusable Zod schema components export const CommonFields = { projectKey: z .string() .describe("Project key - unique identifier for the project") .default(QMETRY_DEFAULTS.PROJECT_KEY), projectKeyOptional: z .string() .optional() .describe("Project key - unique identifier for the project") .default(QMETRY_DEFAULTS.PROJECT_KEY), baseUrl: z .string() .url() .optional() .describe("The base URL for the QMetry instance (must be a valid URL)") .default(QMETRY_DEFAULTS.BASE_URL), start: z .number() .optional() .describe("Start index for pagination - defaults to 0") .default(0), page: z .number() .optional() .describe("Page number to return (starts from 1)") .default(1), limit: z .number() .optional() .describe("Number of records (default 10).") .default(10), tcID: z .number() .describe("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."), id: z .number() .describe("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."), version: z .number() .describe("Test Case version number. " + "This is the internal numeric identifier for the version."), tcVersionID: z .number() .describe("Test Case version number. " + "This is the internal numeric identifier for the version."), versionOptional: z .number() .optional() .describe("Test Case version number (optional, defaults to 1). " + "This is the internal numeric identifier for the version."), rqID: z .number() .describe("Requirement numeric ID (required for fetching specific requirement details). " + "This is the internal numeric identifier, not the entity key like 'MAC-RQ-730'. " + "You can get this ID from requirement search results or by using filters."), rqVersion: z .number() .describe("Requirement version number (required for fetching specific requirement version details). " + "This is the internal numeric identifier for the version."), tcViewId: z .number() .describe("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."), rqViewId: z .number() .describe("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."), rqFolderPath: z .string() .optional() .describe("Folder path for requirements - SYSTEM AUTOMATICALLY SETS TO ROOT. " + 'Leave empty unless you want specific folder. System will automatically use empty string "" (root directory). ' + 'Only specify if user wants specific folder like "Automation/Regression".') .default(""), tcFolderPath: z .string() .optional() .describe("Folder path for test cases - SYSTEM AUTOMATICALLY SETS TO ROOT. " + 'Leave empty unless you want specific folder. System will automatically use empty string "" (root directory). ' + 'Only specify if user wants specific folder like "Automation/Regression".') .default(""), tsFolderPath: z .string() .optional() .describe("Folder path for test suites - SYSTEM AUTOMATICALLY SETS TO ROOT. " + 'Leave empty unless you want specific folder. System will automatically use empty string "" (root directory). ' + 'Only specify if user wants specific folder like "Automation/Regression".') .default(""), folderID: z .number() .optional() .describe("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.)."), tsFolderID: z .number() .describe("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."), tsID: z .number() .describe("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"), gridName: z .string() .optional() .describe("Grid Name to be displayed (default 'TESTEXECUTIONLIST')"), teViewId: z .number() .optional() .describe("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."), tsfeViewId: z .number() .describe("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."), tsViewId: z .number() .describe("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."), tsrunID: z .string() .describe("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"), showTcWithDefects: z .boolean() .optional() .describe("Show test case runs with linked defects") .default(false), entityId: z .number() .describe("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"), getLinked: z .boolean() .optional() .describe("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).") .default(true), istcrFlag: z .boolean() .optional() .describe("Set True for test case run operations") .default(true), scope: z .string() .optional() .describe("Scope of the operation - defines the context for data retrieval. " + "Common values: 'project' (default), 'folder', 'release', 'cycle'. " + "Applies to any entity type being fetched or operated upon.") .default("project"), filter: z .string() .optional() .describe("Filter criteria as JSON string (default '[]')") .default("[]"), udfFilter: z .string() .optional() .describe("User-defined field filter as JSON string (default '[]')") .default("[]"), showRootOnly: z .boolean() .optional() .describe("Whether to show only root folders."), getSubEntities: z .boolean() .optional() .describe("Whether to include sub-entities."), getColumns: z .boolean() .optional() .describe("Whether to get column information in response.") .default(true), hideEmptyFolders: z .boolean() .optional() .describe("Whether to hide empty folders."), folderSortColumn: z .string() .optional() .describe("Folder sort column (default 'name')"), restoreDefaultColumns: z .boolean() .optional() .describe("Whether to restore default columns (default 'false')"), folderSortOrder: z .string() .optional() .describe("Folder sort order (ASC or DESC)"), showArchive: z .boolean() .optional() .describe("Whether to include archived records in the results. " + "When true, returns both active and archived items. " + "When false, returns only active (non-archived) items. " + "Applies to any entity type being fetched (test cases, requirements, releases, cycles, builds, platforms, etc.)."), }; export const ProjectListArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, params: z.object({ showArchive: z .boolean() .optional() .describe("Whether to include archived records in the results. " + "When true, returns both active and archived items. " + "When false, returns only active (non-archived) items. ") .default(false), }), start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, filter: CommonFields.filter, }); export const ProjectArgsSchema = z.object({ projectKey: CommonFields.projectKey, }); export const ReleasesCyclesArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, showArchive: CommonFields.showArchive, }); export const BuildArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, filter: CommonFields.filter, }); export const PlatformArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, sort: z .string() .optional() .describe('Sort criteria as JSON string (default \'[{"property":"platformID","direction":"DESC"}]\')') .default('[{"property":"platformID","direction":"DESC"}]'), filter: CommonFields.filter, }); export const CreateReleaseArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, release: z.object({ name: z.string().describe("Release name (required)"), description: z.string().optional().describe("Release description"), startDate: z .string() .optional() .describe("Release start date in format DD-MM-YYYY or MM-DD-YYYY (depends on QMetry instance date format configuration)"), targetDate: z .string() .optional() .describe("Release target/end date in format DD-MM-YYYY or MM-DD-YYYY (depends on QMetry instance date format configuration)"), projectID: z .number() .optional() .describe("Project ID (optional, can be auto-resolved from project key if not provided)"), }), cycle: z .object({ name: z.string().describe("Cycle name (required if cycle is provided)"), isLocked: z .boolean() .optional() .describe("Whether the cycle is locked (default: false)"), isArchived: z .boolean() .optional() .describe("Whether the cycle is archived (default: false)"), }) .optional() .describe("Optional cycle to create within the release"), }); export const CreateCycleArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, cycle: z.object({ name: z.string().describe("Cycle name (required)"), startDate: z .string() .optional() .describe("Cycle start date in format DD-MM-YYYY or MM-DD-YYYY (depends on QMetry instance date format configuration)"), targetDate: z .string() .optional() .describe("Cycle target/end date in format DD-MM-YYYY or MM-DD-YYYY (depends on QMetry instance date format configuration)"), projectID: z .number() .optional() .describe("Project ID (optional, can be auto-resolved from project key if not provided)"), releaseID: z .number() .describe("Release ID (required) - the release this cycle belongs to"), }), }); export const UpdateCycleArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, cycle: z.object({ name: z.string().optional().describe("Cycle name (optional for update)"), startDate: z .string() .optional() .describe("Cycle start date in format DD-MM-YYYY or MM-DD-YYYY (depends on QMetry instance date format configuration)"), targetDate: z .string() .optional() .describe("Cycle target/end date in format DD-MM-YYYY or MM-DD-YYYY (depends on QMetry instance date format configuration)"), buildID: z .number() .describe("Build ID (required for identifying the cycle to update). " + "To get the buildID - Call API 'Cycle/List' (FETCH_RELEASES_CYCLES tool). " + "From the response, get value of following attribute -> data[<index>].buildID"), releaseID: z .number() .describe("Release ID (required for identifying the cycle to update). " + "To get the releaseID - Call API 'Cycle/List' (FETCH_RELEASES_CYCLES tool). " + "From the response, get value of following attribute -> data[<index>].releaseID"), }), }); export const CreateTestCaseStepSchema = z.object({ orderId: z.number(), description: z.string(), inputData: z.string().optional(), expectedOutcome: z.string().optional(), UDF: z.record(z.string()).optional(), tcStepID: z.number().optional(), // Required for updating existing steps, omit for new steps }); export const UpdateTestCaseRemoveStepSchema = z.object({ tcID: z.number(), projectID: z.number(), tcStepID: z.number(), tcVersionID: z.number(), tcVersion: z.number(), tcsAttCount: z.number(), orderId: z.number(), description: z.string(), inputData: z.string().optional(), expectedOutcome: z.string().optional(), UDF: z.record(z.string()).optional(), tcsIsShared: z.boolean(), tcsIsParameterized: z.boolean(), }); export const CreateTestCaseArgsSchema = z.object({ tcFolderID: z.string(), steps: z.array(CreateTestCaseStepSchema).optional(), name: z.string(), priority: z.number().optional(), component: z.array(z.number()).optional(), testcaseOwner: z.number().optional(), testCaseState: z.number().optional(), testCaseType: z.number().optional(), estimatedTime: z.number().optional(), testingType: z.number().optional(), description: z.string().optional(), associateRelCyc: z.boolean().optional(), releaseCycleMapping: z .array(z.object({ release: z.number(), cycle: z.array(z.number()), version: z.number().optional(), })) .optional(), }); export const UpdateTestCaseArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, tcID: CommonFields.tcID, tcVersionID: CommonFields.tcVersionID, tcVersion: z .number() .optional() .describe("Test Case version number (required when withVersion=true for creating new version). " + "This is the current version number from which a new version will be created."), withVersion: z .boolean() .optional() .describe("Pass 'true' if you want to create a new version of the test case with incremented version number. " + "When true, a new version is created (e.g., if current version is 2, new version 3 is created). " + "When false or omitted, updates the existing version specified by tcVersionID. " + "IMPORTANT: Always send proper tcVersionID to identify which version the request is for."), versionComment: z .string() .optional() .describe("Comment or description for the new version (used only when withVersion=true). " + "Helps track what changed in this new version. Example: 'Updated test steps for new requirements'"), notruncurrent: z .boolean() .optional() .describe("Flag to control execution behavior for current version when creating a new version. " + "Used in conjunction with withVersion=true."), notrunall: z .boolean() .optional() .describe("Flag to control execution behavior for all versions when creating a new version. " + "Used in conjunction with withVersion=true."), folderPath: CommonFields.tsFolderPath, scope: CommonFields.scope, isStepUpdated: z .boolean() .optional() .describe("Set to true when steps are being added, updated, or removed. " + "Required when including 'steps' or 'removeSteps' arrays."), steps: z.array(CreateTestCaseStepSchema).optional(), removeSteps: z.array(UpdateTestCaseRemoveStepSchema).optional(), name: z.string().optional(), priority: z.number().optional(), component: z.array(z.number()).optional(), owner: z.number().optional(), testCaseState: z.number().optional(), testCaseType: z.number().optional(), estimatedTime: z .number() .optional() .describe("Estimated execution time in seconds. Example: 7200 for 2 hours"), executionMinutes: z.number().optional(), testingType: z.number().optional(), description: z.string().optional(), updateOnlyMetadata: z .boolean() .optional() .describe("Set to true to update only metadata fields without touching test steps. " + "When true, steps and removeSteps are ignored."), }); export const TestCaseListArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, viewId: CommonFields.tcViewId, folderPath: CommonFields.tcFolderPath, folderID: CommonFields.folderID, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, scope: CommonFields.scope, showRootOnly: CommonFields.showRootOnly, getSubEntities: CommonFields.getSubEntities, hideEmptyFolders: CommonFields.hideEmptyFolders, folderSortColumn: CommonFields.folderSortColumn, restoreDefaultColumns: CommonFields.restoreDefaultColumns, folderSortOrder: CommonFields.folderSortOrder, filter: CommonFields.filter, udfFilter: CommonFields.udfFilter, }); export const TestCaseDetailsArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, tcID: CommonFields.tcID, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, }); export const TestCaseVersionDetailsArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, id: CommonFields.id, version: CommonFields.version, scope: CommonFields.scope, }); export const TestCaseStepsArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, id: CommonFields.id, version: CommonFields.versionOptional, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, }); export const TestCaseExecutionsArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, tcid: CommonFields.tcID, tcversion: CommonFields.versionOptional, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, scope: CommonFields.scope, filter: CommonFields.filter, }); export const RequirementListArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, viewId: CommonFields.rqViewId, folderPath: CommonFields.rqFolderPath, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, scope: CommonFields.scope, getSubEntities: CommonFields.getSubEntities, hideEmptyFolders: CommonFields.hideEmptyFolders, folderSortColumn: CommonFields.folderSortColumn, folderSortOrder: CommonFields.folderSortOrder, isJiraFilter: z .boolean() .optional() .describe("'false' if using qmetry filter") .default(false), filterType: z .enum(["QMETRY", "JIRA"]) .optional() .describe("Pass 'QMETRY' or 'JIRA'") .default("QMETRY"), filter: CommonFields.filter, udfFilter: CommonFields.udfFilter, sort: z .string() .optional() .describe("Sort Records - refer json schema, Possible property - name, entityKey, associatedVersion, priorityAlias, createdDate, createdByAlias, updatedDate, updatedByAlias, requirementStateAlias, linkedTcCount, linkedDfCount, attachmentCount, createdSystem, owner") .default('[{"property":"name","direction":"ASC"}]'), }); export const RequirementDetailsArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, id: CommonFields.rqID, version: CommonFields.rqVersion, }); export const RequirementsLinkedToTestCaseArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, tcID: CommonFields.tcID, getLinked: z .boolean() .optional() .describe("True to get only requirements that are linked with this test case, " + "false to get requirements which are not linked with this test case. " + "Defaults to true (get linked requirements).") .default(true), start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, rqFolderPath: CommonFields.rqFolderPath, filter: CommonFields.filter, }); export const LinkRequirementToTestCaseArgsSchema = z.object({ tcID: z.string().describe("EntityKey of Testcase (e.g. 'COD-TC-29')"), tcVersionId: CommonFields.tcVersionID, rqVersionIds: z .string() .describe("Comma-separated values of versionId of the Requirement (e.g. '236124,236125')"), }); export const TestCasesLinkedToRequirementArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, rqID: CommonFields.rqID, getLinked: z .boolean() .optional() .describe("True to get only test cases that are linked with this requirement, " + "false to get test cases which are not linked with this requirement. " + "Defaults to true (get linked test cases).") .default(true), showEntityWithReleaseCycle: z .boolean() .optional() .describe("True to list only test cases which have given release and cycle, " + "false for all test cases regardless of release/cycle association. " + "Defaults to false (show all).") .default(false), start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, tcFolderPath: z .string() .optional() .describe("Folder path to get test cases under specific folder. " + 'Use empty string "" for root folder or specify path like "/Sample Template".') .default(""), releaseID: z .string() .optional() .describe("Filter test cases by release ID. " + "Use string representation of release ID (e.g., '7138'). " + "Get release IDs from FETCH_RELEASES_AND_CYCLES tool."), cycleID: z .string() .optional() .describe("Filter test cases by cycle ID. " + "Use string representation of cycle ID (e.g., '13382'). " + "Get cycle IDs from FETCH_RELEASES_AND_CYCLES tool."), filter: CommonFields.filter, getSubEntities: z .boolean() .optional() .describe("Allow filter of sub-entities for requirement.") .default(true), getColumns: z .boolean() .optional() .describe("True to get column information in response.") .default(true), }); export const CreateTestSuiteArgsSchema = z.object({ parentFolderId: z.string(), name: z.string(), isAutomatedFlag: z.boolean().optional(), description: z.string().optional(), testsuiteOwner: z.number().optional(), testSuiteState: z.number().optional(), associateRelCyc: z.boolean().optional(), releaseCycleMapping: z .array(z.object({ buildID: z.number(), releaseId: z.number(), })) .optional(), }); export const UpdateTestSuiteArgsSchema = z.object({ id: z.number().describe("Id of Test Suite to be updated (required)"), TsFolderID: z .number() .describe("Folder ID where Test Suite resides (required)"), entityKey: z .string() .describe("Entity Key of Test Suite to be updated (required)"), name: z.string().optional().describe("Name of the Test Suite"), description: z.string().optional().describe("Description of the Test Suite"), testsuiteOwner: z.number().optional().describe("Owner ID of the Test Suite"), testSuiteState: z.number().optional().describe("State of the Test Suite"), }); export const TestSuiteListArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, viewId: CommonFields.tsViewId, folderPath: CommonFields.tsFolderPath, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, scope: CommonFields.scope, getSubEntities: CommonFields.getSubEntities, filter: CommonFields.filter, udfFilter: CommonFields.udfFilter, sort: z .string() .optional() .describe("Sort Records - refer json schema, Possible property - entityKey, name, testsuiteStatus, linkedPlatformCount, linkedTcCount, createdDate, createdByAlias, updatedDate, updatedByAlias, attachmentCount, owner, remExecutionTime, totalExecutionTime") .default('[{"property":"name","direction":"ASC"}]'), }); export const TestSuitesForTestCaseArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, tsFolderID: CommonFields.tsFolderID, viewId: CommonFields.tsfeViewId.optional(), start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, getColumns: CommonFields.getColumns, filter: CommonFields.filter, }); export const LinkTestCasesToTestSuiteArgsSchema = z .object({ tsID: z.number().describe("Id of Test Suite (required)"), tcvdIDs: z .array(z.number()) .describe("Array of Test Case Version IDs (required if fromReqs is false)"), fromReqs: z .boolean() .optional() .describe("Link TestCases from Requirements (optional, default false)"), }) .strip(); export const RequirementsLinkedTestCasesToTestSuiteArgsSchema = z .object({ tsID: z.number().describe("Id of Test Suite (required)"), tcvdIDs: z .array(z.number()) .describe("Array of Test Case Version IDs (required if fromReqs is true)"), fromReqs: z .boolean() .optional() .describe("Link TestCases from Requirements (optional, default true)"), }) .strip(); export const IssuesLinkedToTestCaseArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, tcID: CommonFields.tcID, getLinked: CommonFields.getLinked.optional().default(true), start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, filter: CommonFields.filter, }); export const TestCasesByTestSuiteArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, tsID: CommonFields.tsID, getLinked: CommonFields.getLinked.optional().default(true), start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, filter: CommonFields.filter, }); export const ExecutionsByTestSuiteArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, tsID: CommonFields.tsID, // API payload param - sent in request body (REQUIRED) tsFolderID: CommonFields.tsFolderID.optional(), gridName: CommonFields.gridName, viewId: CommonFields.teViewId, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, filter: CommonFields.filter, }); export const TestCaseRunsByTestSuiteRunArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, tsrunID: CommonFields.tsrunID, // API payload param - sent in request body (REQUIRED) viewId: CommonFields.teViewId.pipe(z.number()), // API payload param - sent in request body (REQUIRED) start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, }); export const LinkedIssuesByTestCaseRunArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, entityId: CommonFields.entityId, // API payload param - sent in request body (REQUIRED) getLinked: CommonFields.getLinked, getColumns: CommonFields.getColumns, istcrFlag: CommonFields.istcrFlag, start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, filter: CommonFields.filter, }); export const CreateIssueArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, issueType: z.number().describe("Issue type ID (e.g. Bug, Enhancement, etc.)"), issuePriority: z .number() .describe("Issue priority ID (e.g. High, Medium, Low, etc.)"), summary: z.string().describe("Summary or title of the defect/issue"), description: z .string() .optional() .describe("Detailed description of the defect/issue"), sync_with: z .string() .optional() .describe("External system to sync with (e.g. JIRA, QMetry, etc.)"), issueOwner: z.number().optional().describe("Owner/user ID for the issue"), component: z .array(z.number()) .optional() .describe("Component IDs associated with the issue"), affectedRelease: z .array(z.number()) .optional() .describe("Release IDs affected by this issue"), affectedCycles: z .array(z.number()) .optional() .describe("Cycle IDs affected by this issue"), tcRunID: z .number() .optional() .describe("Test Case Run ID to link this defect/issue to a test execution (optional)"), }); export const UpdateIssueArgsSchema = z.object({ DefectId: z.number().describe("ID of the defect/issue to be updated"), entityKey: z .string() .optional() .describe("Entity Key of the defect/issue to be updated"), issueType: z .number() .optional() .describe("Issue type ID (e.g. Bug, Enhancement, etc.)"), issuePriority: z .number() .optional() .describe("Issue priority ID (e.g. High, Medium, Low, etc.)"), summary: z .string() .optional() .describe("Summary or title of the defect/issue"), description: z .string() .optional() .describe("Detailed description of the defect/issue"), issueOwner: z.number().optional().describe("Owner/user ID for the issue"), affectedRelease: z .number() .optional() .describe("Release IDs affected by this issue"), affectedCycles: z .number() .optional() .describe("Cycle IDs affected by this issue"), }); export const IssuesListArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, viewId: z .number() .describe("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."), start: CommonFields.start, page: CommonFields.page, limit: CommonFields.limit, filter: CommonFields.filter, isJiraIntegrated: z .boolean() .optional() .describe("Send true if current project is Integrated with Jira") .default(false), sort: z .string() .optional() .describe("Sort Records - refer json schema, Possible property - entityKey, name, typeAlias, stateAlias, createdDate, createdByAlias, updatedDate, updatedByAlias, priorityAlias, createdSystem, linkedTcrCount, linkedRqCount, dfOwner, attachmentCount, environmentText") .default('[{"property":"name","direction":"ASC"}]'), }); // Export for Link Issues to Testcase Run tool export const LinkIssuesToTestcaseRunArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, issueIds: z .array(z.union([z.string(), z.number()])) .describe("ID of issues to be linked to Testcase Run"), tcrId: z.number().describe("ID of Testcase Run to link issues with"), }); // Export for Link Platforms to Test Suite tool export const LinkPlatformsToTestSuiteArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, qmTsId: z .number() .describe("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"), qmPlatformId: z .string() .describe("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"), }); // Export for Bulk Update Test Case Execution Status tool export const BulkUpdateExecutionStatusArgsSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, entityIDs: z .string() .describe("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"), entityType: z .enum(["TCR", "TCSR"]) .describe("Type of Entity to Execute: 'TCR' (Test Case Run) or 'TCSR' (Test Case Step Run)") .default("TCR"), qmTsRunId: z .string() .describe("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"), runStatusID: z .number() .describe("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."), dropID: z .union([z.number(), z.string()]) .optional() .describe("Unique identifier of drop/build on which execution is to be performed (optional). " + "To get the dropID - Call API 'Fetch Build/List' " + "From the response, get value of following attribute -> data[<index>].dropID"), isAutoExecuted: z .enum(["0", "1"]) .optional() .describe("Set '1' for automated and '0' for manual Execution Type"), isBulkOperation: z .boolean() .optional() .describe("Set true for bulk operations (multiple entityIDs), false for single execution update. " + "Default: true if multiple comma-separated entityIDs, false otherwise"), comments: z .string() .optional() .describe("Optional comments for the execution status update"), username: z .string() .optional() .describe("If Part 11 Compliance is active then required for authentication"), password: z .string() .optional() .describe("If Part 11 Compliance is active then required for authentication"), qmRunObj: z .string() .optional() .describe("Internal QMetry run object (optional, usually empty string)"), type: z .enum(["TCR", "TCSR"]) .optional() .describe("Type of Entity - same as entityType (for backwards compatibility)"), }); /** * Import automation results payload schema * * CRITICAL: File upload is required and must be provided by the user * User should upload a valid result file (.json, .xml, or .zip up to 30 MB) */ export const ImportAutomationResultsPayloadSchema = z.object({ // REQUIRED: File data as base64 string or file path file: z .string() .refine((val) => { // Base64 regex: matches typical base64 strings (not perfect, but covers most cases) const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; // File path regex: ends with .json, .xml, or .zip (case-insensitive) const filePathRegex = /\.(json|xml|zip)$/i; return base64Regex.test(val) || filePathRegex.test(val); }, { message: "Must be a valid base64 string or a file path ending with .json, .xml, or .zip", }) .describe("Base64 encoded file content or file path. User must upload result file (.json, .xml, .zip up to 30 MB)"), // REQUIRED: Original filename with extension fileName: z .string() .describe("Original filename with extension (.json, .xml, or .zip)"), // REQUIRED: Entity type (format of result file) entityType: EntityTypeEnum.describe("Format of result file: TESTNG, CUCUMBER, JUNIT, HPUFT, QAF, or ROBOT"), // OPTIONAL: Automation hierarchy (applies to TestNG and JUnit only) automationHierarchy: AutomationHierarchyEnum.optional().describe("TestNG/JUnit hierarchy: 1=Test Case-Test Step, 2=Test Case only, 3=Test Suite-Test Case. Default: 1"), // OPTIONAL: Test suite name testsuiteName: z .string() .optional() .describe("Custom test suite name. Ignored if automationHierarchy=3 for JUnit or =2 for ROBOT"), // OPTIONAL: Test suite ID (reuse existing) testsuiteId: z .string() .optional() .describe("Reuse existing Test Suite by ID or Entity Key. Ignored if automationHierarchy=3 for JUnit or =2 for ROBOT"), // OPTIONAL: Test suite folder path tsFolderPath: z .string() .optional() .describe("Test suite folder path. Creates folder if doesn't exist. Ignored if reusing test suite"), // OPTIONAL: Test case folder path tcFolderPath: z .string() .optional() .describe("Test case folder path. Creates folder if doesn't exist. Ignored if reusing test case"), // OPTIONAL: Platform ID or name platformID: z .string() .optional() .describe("Platform ID or Platform Name. Default: 'No Platform'"), // OPTIONAL: Project ID or key (overrides header project) projectID: z .string() .optional() .describe("Project ID, Project Key, or Project name. Overrides project in header"), // OPTIONAL: Release ID or name releaseID: z .string() .optional() .describe("Release ID or Release name. Requires projectID if provided"), // OPTIONAL: Cycle ID or name cycleID: z .string() .optional() .describe("Cycle ID or Cycle name. Requires releaseID and projectID if provided"), // OPTIONAL: Build ID or name buildID: z.string().optional().describe("Build ID or Build name"), // OPTIONAL: Test case fields (JSON format) testcase_fields: z .string() .optional() .describe('JSON string with test case system fields and UDFs. Ignored if reusing test case. Example: {"component":["com1"], "priority":"High"}'), // OPTIONAL: Test suite fields (JSON format) testsuite_fields: z .string() .optional() .describe('JSON string with test suite system fields and UDFs. Ignored if reusing test suite. Example: {"testSuiteState":"Open", "testsuiteOwner":"user"}'), // OPTIONAL: Skip warning about summary length skipWarning: SkipWarningEnum.optional().describe("0=Fail if summary >255 chars, 1=Truncate summary to 255 chars. Default: 0"), // OPTIONAL: Matching requirement for test cases is_matching_required: z .string() .optional() .describe("True=Create new TC if summary/steps don't match, False=Reuse linked TC. Default: True"), }); export const FetchAutomationStatusPayloadSchema = z.object({ projectKey: CommonFields.projectKeyOptional, baseUrl: CommonFields.baseUrl, requestID: z .number() .describe("Numeric request ID from import automation response"), });