@uarlouski/testrail-mcp-server 3.0.0 → 3.1.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.
- package/dist/tools/cases/add_case.js +1 -1
- package/dist/tools/cases/fields.js +10 -0
- package/dist/tools/cases/get_case_fields.js +2 -2
- package/dist/tools/cases/get_cases.js +1 -1
- package/dist/tools/cases/types.js +4 -0
- package/dist/tools/cases/update_case.js +1 -1
- package/dist/tools/cases/update_cases.js +1 -1
- package/dist/utils/validator.js +8 -4
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ export const addCaseTool = {
|
|
|
17
17
|
parameters,
|
|
18
18
|
handler: async ({ section_id, title, fields }, client) => {
|
|
19
19
|
if (fields) {
|
|
20
|
-
validateCaseFields(fields, await client.getCaseFields());
|
|
20
|
+
validateCaseFields(fields, await client.getCaseFields(), { allowReadonly: false });
|
|
21
21
|
}
|
|
22
22
|
const caseData = {
|
|
23
23
|
title,
|
|
@@ -45,3 +45,13 @@ export const SYSTEM_FIELDS = [
|
|
|
45
45
|
{ system_name: "refs", label: "References", type: getFieldType(CaseFieldTypeId.String).name, is_required: false, project_scope: { scope: "global" } },
|
|
46
46
|
{ system_name: "labels", label: "Labels", type: getFieldType(CaseFieldTypeId.List).name, is_required: false, project_scope: { scope: "global" }, description: "Use get_labels tool to get available labels/tags." },
|
|
47
47
|
];
|
|
48
|
+
export const READONLY_CASE_FIELDS = [
|
|
49
|
+
"id",
|
|
50
|
+
"suite_id",
|
|
51
|
+
"created_on",
|
|
52
|
+
"updated_on",
|
|
53
|
+
"created_by",
|
|
54
|
+
"updated_by",
|
|
55
|
+
"estimate_forecast",
|
|
56
|
+
"is_deleted",
|
|
57
|
+
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { CaseFieldTypeId, FIELD_TYPES, getFieldType, SYSTEM_FIELDS } from "./fields.js";
|
|
2
|
+
import { CaseFieldTypeId, FIELD_TYPES, getFieldType, SYSTEM_FIELDS, READONLY_CASE_FIELDS } from "./fields.js";
|
|
3
3
|
import { parseDropdownOptions } from "../../utils/mapper.js";
|
|
4
4
|
import { isActive } from "../../utils/sanitizer.js";
|
|
5
5
|
// Field remains optional for backward compatibility with previous implementation.
|
|
@@ -14,7 +14,7 @@ Example: {"priority_id": 2, "template_id": 1, "labels": [1, 2]}
|
|
|
14
14
|
Call get_case_fields with project_id first if field names are not already known.
|
|
15
15
|
Using an unknown field name (e.g. 'label_ids') will result in an error.
|
|
16
16
|
`;
|
|
17
|
-
export { CaseFieldTypeId, FIELD_TYPES, getFieldType, SYSTEM_FIELDS };
|
|
17
|
+
export { CaseFieldTypeId, FIELD_TYPES, getFieldType, SYSTEM_FIELDS, READONLY_CASE_FIELDS };
|
|
18
18
|
function isFieldRequired(field) {
|
|
19
19
|
return field.configs.some(config => config.options?.is_required === true);
|
|
20
20
|
}
|
|
@@ -11,7 +11,7 @@ const parameters = {
|
|
|
11
11
|
}).optional().describe("Section filter configuration. Use query_section to find available sections"),
|
|
12
12
|
filter: z.record(z.string(), z.string()).optional().describe("Optional API-side filters (more efficient for large datasets). Supported: priority_id, type_id, created_by, updated_by, milestone_id, refs, created_after, created_before, updated_after, updated_before. Use comma-separated values for IDs. Example: {\"priority_id\": \"1,2\", \"type_id\": \"3\"}"),
|
|
13
13
|
where: z.record(z.string(), z.any()).optional().describe("Optional client-side filter for any field including custom fields (filters after fetching all cases). Supports exact value matching. Example: {\"custom_automation_status\": 1, \"priority_id\": 2}"),
|
|
14
|
-
fields: z.array(z.string()).optional().describe("Additional fields to include in response beyond id, title, and suite_id
|
|
14
|
+
fields: z.array(z.string()).optional().describe("Additional fields to include in response beyond id, title, and suite_id (supports system fields such as updated_on, created_on, created_by, updated_by, as well as custom fields). Example: [\"updated_on\", \"priority_id\", \"custom_automation_status\"]"),
|
|
15
15
|
output_file: z.string().optional().describe("Absolute file path to save the JSON response to. Use this for large datasets to avoid blowing up context limits."),
|
|
16
16
|
};
|
|
17
17
|
export async function fetchFilteredCases(client, options) {
|
|
@@ -34,10 +34,14 @@ export const CaseSchema = z.object({
|
|
|
34
34
|
priority_id: z.number(),
|
|
35
35
|
milestone_id: z.number().nullable(),
|
|
36
36
|
refs: z.string().nullable(),
|
|
37
|
+
created_by: z.number().optional(),
|
|
37
38
|
created_on: z.number(),
|
|
39
|
+
updated_by: z.number().optional(),
|
|
38
40
|
updated_on: z.number(),
|
|
39
41
|
estimate: z.string().nullable(),
|
|
42
|
+
estimate_forecast: z.string().nullable().optional(),
|
|
40
43
|
suite_id: z.number(),
|
|
44
|
+
is_deleted: z.number().optional(),
|
|
41
45
|
labels: z.array(LabelSchema),
|
|
42
46
|
}).loose();
|
|
43
47
|
export const CaseHistoryChangeSchema = z.object({
|
|
@@ -17,7 +17,7 @@ export const updateCaseTool = {
|
|
|
17
17
|
description: description.trim(),
|
|
18
18
|
parameters,
|
|
19
19
|
handler: async ({ case_id, fields }, client) => {
|
|
20
|
-
validateCaseFields(fields, await client.getCaseFields());
|
|
20
|
+
validateCaseFields(fields, await client.getCaseFields(), { allowReadonly: false });
|
|
21
21
|
const id = normalizeEntityId(case_id);
|
|
22
22
|
const updatedCase = await client.updateCase(id, fields);
|
|
23
23
|
return {
|
|
@@ -16,7 +16,7 @@ export const updateCasesTool = {
|
|
|
16
16
|
description: description.trim(),
|
|
17
17
|
parameters,
|
|
18
18
|
handler: async ({ case_ids, fields }, client) => {
|
|
19
|
-
validateCaseFields(fields, await client.getCaseFields());
|
|
19
|
+
validateCaseFields(fields, await client.getCaseFields(), { allowReadonly: false });
|
|
20
20
|
const caseData = await client.getCase(case_ids[0]);
|
|
21
21
|
const updatedCases = await client.updateCases(caseData.suite_id, case_ids, fields);
|
|
22
22
|
return {
|
package/dist/utils/validator.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SYSTEM_FIELDS, mapToFieldSchema } from "../tools/cases/get_case_fields.js";
|
|
1
|
+
import { SYSTEM_FIELDS, READONLY_CASE_FIELDS, mapToFieldSchema } from "../tools/cases/get_case_fields.js";
|
|
2
2
|
import { isActive } from "./sanitizer.js";
|
|
3
3
|
const SUITE_MODE_SINGLE_WITH_BASELINES = 2;
|
|
4
4
|
const SUITE_MODE_MULTI = 3;
|
|
@@ -21,16 +21,20 @@ export async function validateSuiteId(client, projectId, suiteId) {
|
|
|
21
21
|
*
|
|
22
22
|
* @param fields A record of fields or array of field names to validate
|
|
23
23
|
* @param caseFields Available custom case fields from TestRail
|
|
24
|
+
* @param options Validation options (e.g. allowReadonly)
|
|
24
25
|
*/
|
|
25
|
-
export function validateCaseFields(fields, caseFields) {
|
|
26
|
+
export function validateCaseFields(fields, caseFields, options = { allowReadonly: true }) {
|
|
26
27
|
const fieldKeys = Array.isArray(fields) ? fields : Object.keys(fields);
|
|
27
28
|
if (fieldKeys.length === 0) {
|
|
28
29
|
return;
|
|
29
30
|
}
|
|
30
31
|
const customFieldSchemas = caseFields.filter(isActive).map(mapToFieldSchema);
|
|
31
32
|
const validFieldNames = new Set([...SYSTEM_FIELDS, ...customFieldSchemas].map(f => f.system_name));
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
if (options.allowReadonly !== false) {
|
|
34
|
+
for (const field of READONLY_CASE_FIELDS) {
|
|
35
|
+
validFieldNames.add(field);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
34
38
|
const invalidFields = [];
|
|
35
39
|
for (const key of fieldKeys) {
|
|
36
40
|
if (!validFieldNames.has(key)) {
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "3.
|
|
1
|
+
export const VERSION = "3.1.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uarlouski/testrail-mcp-server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"mcpName": "io.github.uarlouski/testrail-mcp-server",
|
|
5
5
|
"description": "Model Context Protocol (MCP) server for TestRail: let Claude, Cursor, and Windsurf manage test cases, runs, results, and attachments.",
|
|
6
6
|
"main": "dist/index.js",
|