@uarlouski/testrail-mcp-server 2.1.0 → 2.2.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/index.js CHANGED
@@ -23,7 +23,7 @@ if (!parseResult.success) {
23
23
  const { TESTRAIL_INSTANCE_URL, TESTRAIL_USERNAME, TESTRAIL_API_KEY, TESTRAIL_ENABLE_SHARED_STEPS, TESTRAIL_ALLOW_WRITE_OPERATIONS, TESTRAIL_ALLOW_READ_OPERATIONS, TESTRAIL_ALLOW_DELETE_OPERATIONS } = parseResult.data;
24
24
  const server = new McpServer({
25
25
  name: "TestRail MCP Server",
26
- version: "2.1.0",
26
+ version: "2.2.0",
27
27
  });
28
28
  const client = new TestRailClient(TESTRAIL_INSTANCE_URL, TESTRAIL_USERNAME, TESTRAIL_API_KEY);
29
29
  const tools = getToolsToRegister({
@@ -13,35 +13,41 @@ Example: {"priority_id": 2, "template_id": 1, "labels": [1, 2]}
13
13
  Call get_case_fields with project_id first if field names are not already known.
14
14
  Using an unknown field name (e.g. 'label_ids') will result in an error.
15
15
  `;
16
- const FIELD_TYPE_NAMES = {
17
- 1: "String",
18
- 2: "Integer",
19
- 3: "Text",
20
- 4: "URL",
21
- 5: "Checkbox",
22
- 6: "Dropdown",
23
- 7: "User",
24
- 8: "Date",
25
- 9: "Milestone",
26
- 10: "Steps",
27
- 11: "Step Results",
28
- 12: "Multi-select",
29
- 13: "Scenarios",
30
- 14: "List",
31
- };
32
- function getFieldTypeName(typeId) {
33
- return FIELD_TYPE_NAMES[typeId] || `Unknown (${typeId})`;
16
+ export const FIELD_TYPES = [
17
+ { typeId: 1, name: "String" },
18
+ { typeId: 2, name: "Integer" },
19
+ { typeId: 3, name: "Text" },
20
+ { typeId: 4, name: "URL" },
21
+ { typeId: 5, name: "Checkbox", description: "Boolean value, either true or false." },
22
+ { typeId: 6, name: "Dropdown" },
23
+ { typeId: 7, name: "User" },
24
+ { typeId: 8, name: "Date" },
25
+ { typeId: 9, name: "Milestone" },
26
+ { typeId: 10, name: "Steps" },
27
+ { typeId: 11, name: "Step Results" },
28
+ { typeId: 12, name: "Multi-select" },
29
+ { typeId: 13, name: "Scenarios" },
30
+ { typeId: 14, name: "List" },
31
+ ];
32
+ function getFieldType(typeId) {
33
+ const found = FIELD_TYPES.find(t => t.typeId === typeId);
34
+ return found ?? { typeId, name: `Unknown (${typeId})` };
34
35
  }
35
36
  function isFieldRequired(field) {
36
37
  return field.configs.some(config => config.options?.is_required === true);
37
38
  }
38
39
  export function mapToFieldSchema(field) {
40
+ const fieldType = getFieldType(field.type_id);
41
+ const descriptions = [field.description, fieldType.description].filter(Boolean);
39
42
  const schema = {
40
43
  system_name: field.system_name,
41
44
  label: field.label,
42
- type: getFieldTypeName(field.type_id),
45
+ type: fieldType.name,
43
46
  is_required: isFieldRequired(field),
44
47
  };
48
+ if (descriptions.length > 0) {
49
+ schema.description = descriptions.join(" ");
50
+ }
45
51
  if (!field.include_all && field.template_ids.length > 0) {
46
52
  schema.template_ids = field.template_ids;
47
53
  }
@@ -68,15 +74,15 @@ function isFieldForProject(field, projectId) {
68
74
  return contexts.some(ctx => ctx.is_global || ctx.project_ids.includes(projectId));
69
75
  }
70
76
  export const SYSTEM_FIELDS = [
71
- { system_name: "title", label: "Title", type: "String", is_required: true, project_scope: { scope: "global" } },
72
- { system_name: "section_id", label: "Section", type: "Integer", is_required: true, project_scope: { scope: "global" } },
73
- { system_name: "template_id", label: "Template", type: "Integer", is_required: false, project_scope: { scope: "global" } },
74
- { system_name: "type_id", label: "Type", type: "Integer", is_required: false, project_scope: { scope: "global" } },
75
- { system_name: "priority_id", label: "Priority", type: "Integer", is_required: false, project_scope: { scope: "global" } },
76
- { system_name: "estimate", label: "Estimate", type: "String", is_required: false, project_scope: { scope: "global" } },
77
- { system_name: "milestone_id", label: "Milestone", type: "Integer", is_required: false, project_scope: { scope: "global" } },
78
- { system_name: "refs", label: "References", type: "String", is_required: false, project_scope: { scope: "global" } },
79
- { system_name: "labels", label: "Labels", type: "List", is_required: false, project_scope: { scope: "global" }, comment: "Use get_labels tool to get available labels/tags." },
77
+ { system_name: "title", label: "Title", type: getFieldType(1).name, is_required: true, project_scope: { scope: "global" } },
78
+ { system_name: "section_id", label: "Section", type: getFieldType(2).name, is_required: true, project_scope: { scope: "global" } },
79
+ { system_name: "template_id", label: "Template", type: getFieldType(2).name, is_required: false, project_scope: { scope: "global" } },
80
+ { system_name: "type_id", label: "Type", type: getFieldType(2).name, is_required: false, project_scope: { scope: "global" } },
81
+ { system_name: "priority_id", label: "Priority", type: getFieldType(2).name, is_required: false, project_scope: { scope: "global" } },
82
+ { system_name: "estimate", label: "Estimate", type: getFieldType(1).name, is_required: false, project_scope: { scope: "global" } },
83
+ { system_name: "milestone_id", label: "Milestone", type: getFieldType(2).name, is_required: false, project_scope: { scope: "global" } },
84
+ { system_name: "refs", label: "References", type: getFieldType(1).name, is_required: false, project_scope: { scope: "global" } },
85
+ { system_name: "labels", label: "Labels", type: getFieldType(14).name, is_required: false, project_scope: { scope: "global" }, description: "Use get_labels tool to get available labels/tags." },
80
86
  ];
81
87
  const description = `
82
88
  Get the field schema for test cases for a specific project.
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { SectionSchema, CreateSectionSchema, UpdateSectionSchema } from "./types.js";
3
3
  import { handleMutate } from "../../utils/mutate_handler.js";
4
+ import { validateSuiteId } from "../../utils/validator.js";
4
5
  const parameters = {
5
6
  payload: z.discriminatedUnion("action", [
6
7
  CreateSectionSchema,
@@ -15,6 +16,7 @@ export const mutateSectionTool = {
15
16
  handler: async (args, client) => {
16
17
  const { payload } = args;
17
18
  return handleMutate(payload, async (createPayload) => {
19
+ await validateSuiteId(client, createPayload.project_id, createPayload.suite_id);
18
20
  const { project_id, action, ...data } = createPayload;
19
21
  return client.addSection(project_id, data);
20
22
  }, async (updatePayload) => {
@@ -13,6 +13,7 @@ export const BaseSectionFields = z.object({
13
13
  export const CreateSectionSchema = BaseSectionFields.extend({
14
14
  action: z.literal("create").describe("The operation to perform: create a new section"),
15
15
  project_id: z.number().describe("The ID of the project the section should be added to"),
16
+ suite_id: z.number().optional().describe("The ID of the test suite (required for multi-suite projects, i.e. suite_mode=2 or 3). Use get_suites to find available suites"),
16
17
  parent_id: z.number().optional().describe("The ID of the parent section (to build section hierarchies)"),
17
18
  });
18
19
  export const UpdateSectionSchema = BaseSectionFields.partial().extend({
@@ -1,16 +1,17 @@
1
1
  import { SYSTEM_FIELDS, mapToFieldSchema } from "../tools/cases/get_case_fields.js";
2
2
  import { isActive } from "./sanitizer.js";
3
+ const SUITE_MODE_SINGLE_WITH_BASELINES = 2;
3
4
  const SUITE_MODE_MULTI = 3;
4
5
  /**
5
- * Validates that suite_id is provided when the project uses multiple test suites (suite_mode=3).
6
+ * Validates that suite_id is provided when the project uses multiple test suites or baselines (suite_mode=2 or 3).
6
7
  * Throws a descriptive Error if suite_id is required but missing.
7
8
  */
8
9
  export async function validateSuiteId(client, projectId, suiteId) {
9
10
  if (suiteId)
10
11
  return;
11
12
  const project = await client.getProject(projectId);
12
- if (project.suite_mode === SUITE_MODE_MULTI) {
13
- throw new Error(`Project "${project.name}" (id: ${project.id}) uses multiple test suites (suite_mode=3). ` +
13
+ if (project.suite_mode === SUITE_MODE_MULTI || project.suite_mode === SUITE_MODE_SINGLE_WITH_BASELINES) {
14
+ throw new Error(`Project "${project.name}" (id: ${project.id}) uses multiple test suites/baselines (suite_mode=${project.suite_mode}). ` +
14
15
  `The suite_id parameter is required. Use get_suites to find available suites for this project.`);
15
16
  }
16
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uarlouski/testrail-mcp-server",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "mcpName": "io.github.uarlouski/testrail-mcp-server",
5
5
  "description": "Model Context Protocol (MCP) server for TestRail integration, enabling LLMs to fetch test cases and metadata.",
6
6
  "main": "dist/index.js",