@uarlouski/testrail-mcp-server 1.11.0 → 2.0.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/README.md +24 -11
- package/dist/client/testrail.js +47 -0
- package/dist/index.js +14 -55
- package/dist/tools/{add_case.js → cases/add_case.js} +2 -1
- package/dist/tools/{get_case.js → cases/get_case.js} +3 -2
- package/dist/tools/{get_case_fields.js → cases/get_case_fields.js} +5 -3
- package/dist/tools/{get_cases.js → cases/get_cases.js} +2 -1
- package/dist/tools/cases/index.js +14 -0
- package/dist/tools/cases/types.js +40 -0
- package/dist/tools/{update_case.js → cases/update_case.js} +2 -1
- package/dist/tools/{update_cases.js → cases/update_cases.js} +2 -1
- package/dist/tools/commons/delete_entity.js +24 -0
- package/dist/tools/commons/get_configurations.js +17 -0
- package/dist/tools/{get_labels.js → commons/get_labels.js} +2 -1
- package/dist/tools/{get_priorities.js → commons/get_priorities.js} +2 -1
- package/dist/tools/{get_statuses.js → commons/get_statuses.js} +2 -1
- package/dist/tools/{get_templates.js → commons/get_templates.js} +2 -1
- package/dist/tools/{get_tests.js → commons/get_tests.js} +2 -1
- package/dist/tools/{get_users.js → commons/get_users.js} +2 -1
- package/dist/tools/commons/index.js +18 -0
- package/dist/tools/commons/types.js +49 -0
- package/dist/tools/projects/index.js +4 -0
- package/dist/tools/projects/query_project.js +29 -0
- package/dist/tools/projects/types.js +14 -0
- package/dist/tools/registry.js +46 -0
- package/dist/tools/{add_results.js → results/add_results.js} +2 -1
- package/dist/tools/{add_results_for_cases.js → results/add_results_for_cases.js} +2 -1
- package/dist/tools/results/get_results.js +18 -0
- package/dist/tools/results/index.js +8 -0
- package/dist/tools/results/types.js +8 -0
- package/dist/tools/{add_attachment_to_run.js → runs/add_attachment_to_run.js} +2 -1
- package/dist/tools/runs/index.js +8 -0
- package/dist/tools/runs/mutate_run.js +40 -0
- package/dist/tools/runs/query_run.js +35 -0
- package/dist/tools/runs/types.js +43 -0
- package/dist/tools/{get_sections.js → sections/get_sections.js} +2 -1
- package/dist/tools/sections/index.js +6 -0
- package/dist/tools/sections/mutate_section.js +25 -0
- package/dist/tools/sections/types.js +21 -0
- package/dist/tools/shared_steps/add_shared_step.js +2 -1
- package/dist/tools/shared_steps/get_shared_step.js +2 -1
- package/dist/tools/shared_steps/get_shared_step_history.js +2 -1
- package/dist/tools/shared_steps/get_shared_steps.js +2 -1
- package/dist/tools/shared_steps/index.js +12 -0
- package/dist/tools/shared_steps/types.js +18 -0
- package/dist/tools/shared_steps/update_shared_step.js +2 -1
- package/dist/tools/suites/index.js +6 -0
- package/dist/tools/suites/mutate_suite.js +25 -0
- package/dist/tools/suites/query_suite.js +28 -0
- package/dist/tools/suites/types.js +28 -0
- package/dist/utils/mutate_handler.js +22 -0
- package/dist/utils/query_handler.js +20 -0
- package/dist/utils/validator.js +1 -1
- package/package.json +1 -1
- package/dist/tools/add_run.js +0 -19
- package/dist/tools/get_projects.js +0 -15
- package/dist/tools/shared_steps/delete_shared_step.js +0 -18
- package/dist/types/testrail.js +0 -130
package/README.md
CHANGED
|
@@ -139,14 +139,17 @@ Once configured, turbo-charge your QA workflow by asking your AI assistant:
|
|
|
139
139
|
|
|
140
140
|
---
|
|
141
141
|
|
|
142
|
-
## ⚙️ Environment Variables
|
|
143
|
-
|
|
144
|
-
| Variable | Description | Required |
|
|
145
|
-
|
|
146
|
-
| `TESTRAIL_INSTANCE_URL` | Your TestRail instance URL (e.g., `https://example.testrail.io`) | ✅ |
|
|
147
|
-
| `TESTRAIL_USERNAME` | Your TestRail user email address | ✅ |
|
|
148
|
-
| `TESTRAIL_API_KEY` | Your TestRail API key ([Guide](https://support.testrail.com/hc/en-us/articles/7077039051412-Accessing-the-TestRail-API)) | ✅ |
|
|
149
|
-
| `TESTRAIL_ENABLE_SHARED_STEPS` | Set to `true` to enable Shared Steps management tools | |
|
|
142
|
+
## ⚙️ Environment Variables & Security Controls
|
|
143
|
+
|
|
144
|
+
| Variable | Description | Required | Default |
|
|
145
|
+
|----------|-------------|:--------:|:-------:|
|
|
146
|
+
| `TESTRAIL_INSTANCE_URL` | Your TestRail instance URL (e.g., `https://example.testrail.io`) | ✅ | |
|
|
147
|
+
| `TESTRAIL_USERNAME` | Your TestRail user email address | ✅ | |
|
|
148
|
+
| `TESTRAIL_API_KEY` | Your TestRail API key ([Guide](https://support.testrail.com/hc/en-us/articles/7077039051412-Accessing-the-TestRail-API)) | ✅ | |
|
|
149
|
+
| `TESTRAIL_ENABLE_SHARED_STEPS` | Set to `true` to enable Shared Steps management tools | | `false` |
|
|
150
|
+
| `TESTRAIL_ALLOW_WRITE_OPERATIONS` | Allow write operations (e.g. adding/updating test cases, test runs, sections) | | `true` |
|
|
151
|
+
| `TESTRAIL_ALLOW_READ_OPERATIONS` | Allow read operations (e.g. retrieving projects, test cases, templates) | | `true` |
|
|
152
|
+
| `TESTRAIL_ALLOW_DELETE_OPERATIONS` | Allow delete operations (e.g. deleting cases or shared steps). Enabled strictly via `true`. | | `false` |
|
|
150
153
|
|
|
151
154
|
---
|
|
152
155
|
|
|
@@ -161,8 +164,11 @@ The TestRail MCP Server provides heavily typed, descriptive tools designed speci
|
|
|
161
164
|
### 🔭 Discovery & Navigation
|
|
162
165
|
| Tool | Functionality |
|
|
163
166
|
|------|-------------|
|
|
164
|
-
| `
|
|
167
|
+
| `query_project` | Retrieve a single project (`action: "one"`) or all active projects (`action: "many"`). |
|
|
168
|
+
| `query_suite` | Retrieve a single test suite (`action: "one"`) or all test suites for a project (`action: "many"`). |
|
|
165
169
|
| `get_sections` | Navigate the precise folder/section hierarchy of any test suite. |
|
|
170
|
+
| `mutate_suite` | Create a new test suite or update an existing one in TestRail. |
|
|
171
|
+
| `mutate_section` | Create a new section or update an existing section in TestRail. |
|
|
166
172
|
| `get_users` | Retrieve active users. Handles per-project filtering and automatic fallback polling for non-admin accounts. |
|
|
167
173
|
|
|
168
174
|
### 📋 Test Case Management
|
|
@@ -177,8 +183,10 @@ The TestRail MCP Server provides heavily typed, descriptive tools designed speci
|
|
|
177
183
|
### ▶️ Test Execution & Tracking
|
|
178
184
|
| Tool | Functionality |
|
|
179
185
|
|------|-------------|
|
|
180
|
-
| `
|
|
186
|
+
| `query_run` | Retrieve a single test run (`action: "one"`) or all test runs for a project (`action: "many"`, supporting filters). |
|
|
187
|
+
| `mutate_run` | Create a new test run or update an existing one in TestRail. |
|
|
181
188
|
| `get_tests` | Retrieve individual tests for a specific test run, with optional status filtering. |
|
|
189
|
+
| `get_results` | Retrieve paginated results for a specific test ID. |
|
|
182
190
|
| `add_results` | Submit test results to a test run using the specific `test_id`. |
|
|
183
191
|
| `add_results_for_cases` | Submits results to a run mapping directly to `case_id`s, streamlining automation workflows. |
|
|
184
192
|
| `add_attachment_to_run` | Attach logs, files, or zipped artifacts directly to an ongoing test run. |
|
|
@@ -191,7 +199,11 @@ The TestRail MCP Server provides heavily typed, descriptive tools designed speci
|
|
|
191
199
|
| `get_shared_step_history` | View the complete audit trail and version history of a shared step set. |
|
|
192
200
|
| `add_shared_step` | Create a new reusable set of shared test steps. |
|
|
193
201
|
| `update_shared_step` | Modify an existing shared step set (changes propagate to all linked test cases). |
|
|
194
|
-
|
|
202
|
+
|
|
203
|
+
### 🗑️ Deletion
|
|
204
|
+
| Tool | Functionality |
|
|
205
|
+
|------|-------------|
|
|
206
|
+
| `delete_entity` | Deletes a specified TestRail entity (supports `"case"` or `"shared_step"`) by its ID. Requires `TESTRAIL_ALLOW_DELETE_OPERATIONS` to be set to `true` in your environment. |
|
|
195
207
|
|
|
196
208
|
### 🧠 System Metadata
|
|
197
209
|
| Tool | Functionality |
|
|
@@ -200,6 +212,7 @@ The TestRail MCP Server provides heavily typed, descriptive tools designed speci
|
|
|
200
212
|
| `get_priorities` | Retrieve priority levels configured within your instance structure. |
|
|
201
213
|
| `get_case_fields` | Discover custom field definitions, formats, and UI dropdown options. Supports filtering by `project_id`. |
|
|
202
214
|
| `get_templates` | Identify available case templates to mandate correct AI structuring. |
|
|
215
|
+
| `get_configurations` | Retrieve all configuration groups and configurations for a project. |
|
|
203
216
|
|
|
204
217
|
---
|
|
205
218
|
|
package/dist/client/testrail.js
CHANGED
|
@@ -102,10 +102,54 @@ export class TestRailClient {
|
|
|
102
102
|
async addRun(projectId, fields) {
|
|
103
103
|
return this.post(`${API_BASE_V2}/add_run/${projectId}`, fields);
|
|
104
104
|
}
|
|
105
|
+
async updateRun(runId, fields) {
|
|
106
|
+
return this.post(`${API_BASE_V2}/update_run/${runId}`, fields);
|
|
107
|
+
}
|
|
105
108
|
async getSections(projectId) {
|
|
106
109
|
const url = `${API_BASE_V2}/get_sections/${projectId}`;
|
|
107
110
|
return this.paginateAll(url, 'sections');
|
|
108
111
|
}
|
|
112
|
+
async addSection(projectId, data) {
|
|
113
|
+
return this.post(`${API_BASE_V2}/add_section/${projectId}`, data);
|
|
114
|
+
}
|
|
115
|
+
async updateSection(sectionId, data) {
|
|
116
|
+
return this.post(`${API_BASE_V2}/update_section/${sectionId}`, data);
|
|
117
|
+
}
|
|
118
|
+
async addSuite(projectId, data) {
|
|
119
|
+
return this.post(`${API_BASE_V2}/add_suite/${projectId}`, data);
|
|
120
|
+
}
|
|
121
|
+
async updateSuite(suiteId, data) {
|
|
122
|
+
return this.post(`${API_BASE_V2}/update_suite/${suiteId}`, data);
|
|
123
|
+
}
|
|
124
|
+
async getSuite(suiteId) {
|
|
125
|
+
return this.get(`${API_BASE_V2}/get_suite/${suiteId}`);
|
|
126
|
+
}
|
|
127
|
+
async getSuites(projectId) {
|
|
128
|
+
const url = `${API_BASE_V2}/get_suites/${projectId}`;
|
|
129
|
+
return this.paginateAll(url, 'suites');
|
|
130
|
+
}
|
|
131
|
+
async getRun(runId) {
|
|
132
|
+
return this.get(`${API_BASE_V2}/get_run/${runId}`);
|
|
133
|
+
}
|
|
134
|
+
async getRuns(projectId, filter) {
|
|
135
|
+
let url = `${API_BASE_V2}/get_runs/${projectId}`;
|
|
136
|
+
if (filter) {
|
|
137
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
138
|
+
url += `&${key}=${encodeURIComponent(value)}`;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return this.paginateAll(url, 'runs');
|
|
142
|
+
}
|
|
143
|
+
async getResults(testId) {
|
|
144
|
+
const url = `${API_BASE_V2}/get_results/${testId}`;
|
|
145
|
+
return this.paginateAll(url, 'results');
|
|
146
|
+
}
|
|
147
|
+
async getConfigs(projectId) {
|
|
148
|
+
return this.get(`${API_BASE_V2}/get_configs/${projectId}`);
|
|
149
|
+
}
|
|
150
|
+
async getProject(projectId) {
|
|
151
|
+
return this.get(`${API_BASE_V2}/get_project/${projectId}`);
|
|
152
|
+
}
|
|
109
153
|
async getProjects() {
|
|
110
154
|
if (!this.projectsPromise) {
|
|
111
155
|
this.projectsPromise = this.get(`${API_BASE_V2}/get_projects`)
|
|
@@ -159,6 +203,9 @@ export class TestRailClient {
|
|
|
159
203
|
async updateSharedStep(sharedStepId, data) {
|
|
160
204
|
return this.post(`${API_BASE_V2}/update_shared_step/${sharedStepId}`, data);
|
|
161
205
|
}
|
|
206
|
+
async deleteCase(caseId) {
|
|
207
|
+
return this._executeRequest('POST', `${API_BASE_V2}/delete_case/${caseId}`, this.headers, JSON.stringify({}), false);
|
|
208
|
+
}
|
|
162
209
|
async deleteSharedStep(sharedStepId) {
|
|
163
210
|
return this._executeRequest('POST', `${API_BASE_V2}/delete_shared_step/${sharedStepId}`, this.headers, JSON.stringify({}), false);
|
|
164
211
|
}
|
package/dist/index.js
CHANGED
|
@@ -3,81 +3,40 @@ import "dotenv/config";
|
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import { TestRailClient } from "./client/testrail.js";
|
|
6
|
-
import {
|
|
7
|
-
import { getCasesTool } from "./tools/get_cases.js";
|
|
8
|
-
import { getCaseFieldsTool } from "./tools/get_case_fields.js";
|
|
9
|
-
import { getTemplatesTool } from "./tools/get_templates.js";
|
|
10
|
-
import { updateCaseTool } from "./tools/update_case.js";
|
|
11
|
-
import { updateCasesTool } from "./tools/update_cases.js";
|
|
12
|
-
import { addCaseTool } from "./tools/add_case.js";
|
|
13
|
-
import { getSectionsTool } from "./tools/get_sections.js";
|
|
14
|
-
import { getProjectsTool } from "./tools/get_projects.js";
|
|
15
|
-
import { addRunTool } from "./tools/add_run.js";
|
|
16
|
-
import { getStatusesTool } from "./tools/get_statuses.js";
|
|
17
|
-
import { getPrioritiesTool } from "./tools/get_priorities.js";
|
|
18
|
-
import { getTestsTool } from "./tools/get_tests.js";
|
|
19
|
-
import { addResultsTool } from "./tools/add_results.js";
|
|
20
|
-
import { addAttachmentToRunTool } from "./tools/add_attachment_to_run.js";
|
|
21
|
-
import { addResultsForCasesTool } from "./tools/add_results_for_cases.js";
|
|
22
|
-
import { getLabelsTool } from "./tools/get_labels.js";
|
|
23
|
-
import { getUsersTool } from "./tools/get_users.js";
|
|
24
|
-
import { getSharedStepsTool } from "./tools/shared_steps/get_shared_steps.js";
|
|
25
|
-
import { getSharedStepTool } from "./tools/shared_steps/get_shared_step.js";
|
|
26
|
-
import { getSharedStepHistoryTool } from "./tools/shared_steps/get_shared_step_history.js";
|
|
27
|
-
import { addSharedStepTool } from "./tools/shared_steps/add_shared_step.js";
|
|
28
|
-
import { updateSharedStepTool } from "./tools/shared_steps/update_shared_step.js";
|
|
29
|
-
import { deleteSharedStepTool } from "./tools/shared_steps/delete_shared_step.js";
|
|
6
|
+
import { getToolsToRegister } from "./tools/registry.js";
|
|
30
7
|
import { removeNullish } from "./utils/sanitizer.js";
|
|
31
8
|
import z from "zod";
|
|
32
9
|
const EnvSchema = z.object({
|
|
33
10
|
TESTRAIL_INSTANCE_URL: z.url('Must be a valid TestRail URL'),
|
|
34
11
|
TESTRAIL_USERNAME: z.email('Must be a valid email address'),
|
|
35
12
|
TESTRAIL_API_KEY: z.string().min(1, 'API key is required'),
|
|
36
|
-
TESTRAIL_ENABLE_SHARED_STEPS: z.string().optional().transform(val => val === 'true')
|
|
13
|
+
TESTRAIL_ENABLE_SHARED_STEPS: z.string().optional().transform(val => val === 'true'),
|
|
14
|
+
TESTRAIL_ALLOW_WRITE_OPERATIONS: z.string().optional().transform(val => val === undefined ? true : val === 'true'),
|
|
15
|
+
TESTRAIL_ALLOW_READ_OPERATIONS: z.string().optional().transform(val => val === undefined ? true : val === 'true'),
|
|
16
|
+
TESTRAIL_ALLOW_DELETE_OPERATIONS: z.string().optional().transform(val => val === 'true')
|
|
37
17
|
});
|
|
38
18
|
const parseResult = EnvSchema.safeParse(process.env);
|
|
39
19
|
if (!parseResult.success) {
|
|
40
20
|
console.error("Invalid TestRail environment configuration:", JSON.stringify(z.treeifyError(parseResult.error), null, 2));
|
|
41
21
|
process.exit(1);
|
|
42
22
|
}
|
|
43
|
-
const { TESTRAIL_INSTANCE_URL, TESTRAIL_USERNAME, TESTRAIL_API_KEY, TESTRAIL_ENABLE_SHARED_STEPS } = parseResult.data;
|
|
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;
|
|
44
24
|
const server = new McpServer({
|
|
45
25
|
name: "TestRail MCP Server",
|
|
46
|
-
version: "
|
|
26
|
+
version: "2.0.0",
|
|
47
27
|
});
|
|
48
28
|
const client = new TestRailClient(TESTRAIL_INSTANCE_URL, TESTRAIL_USERNAME, TESTRAIL_API_KEY);
|
|
49
|
-
const tools =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
getSectionsTool,
|
|
56
|
-
updateCaseTool,
|
|
57
|
-
updateCasesTool,
|
|
58
|
-
addCaseTool,
|
|
59
|
-
addRunTool,
|
|
60
|
-
getStatusesTool,
|
|
61
|
-
getPrioritiesTool,
|
|
62
|
-
getTestsTool,
|
|
63
|
-
addResultsTool,
|
|
64
|
-
addResultsForCasesTool,
|
|
65
|
-
addAttachmentToRunTool,
|
|
66
|
-
getLabelsTool,
|
|
67
|
-
getUsersTool,
|
|
68
|
-
];
|
|
69
|
-
if (TESTRAIL_ENABLE_SHARED_STEPS) {
|
|
70
|
-
tools.push(getSharedStepsTool);
|
|
71
|
-
tools.push(getSharedStepTool);
|
|
72
|
-
tools.push(getSharedStepHistoryTool);
|
|
73
|
-
tools.push(addSharedStepTool);
|
|
74
|
-
tools.push(updateSharedStepTool);
|
|
75
|
-
tools.push(deleteSharedStepTool);
|
|
76
|
-
}
|
|
29
|
+
const tools = getToolsToRegister({
|
|
30
|
+
enableSharedSteps: TESTRAIL_ENABLE_SHARED_STEPS,
|
|
31
|
+
allowWrite: TESTRAIL_ALLOW_WRITE_OPERATIONS,
|
|
32
|
+
allowRead: TESTRAIL_ALLOW_READ_OPERATIONS,
|
|
33
|
+
allowDelete: TESTRAIL_ALLOW_DELETE_OPERATIONS
|
|
34
|
+
});
|
|
77
35
|
for (const tool of tools) {
|
|
78
36
|
server.registerTool(tool.name, {
|
|
79
37
|
description: tool.description,
|
|
80
38
|
inputSchema: tool.parameters,
|
|
39
|
+
annotations: tool.annotations,
|
|
81
40
|
}, async (args) => {
|
|
82
41
|
try {
|
|
83
42
|
const output = await tool.handler(args, client);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { validateCaseFields } from "
|
|
2
|
+
import { validateCaseFields } from "../../utils/validator.js";
|
|
3
3
|
import { CASE_FIELDS_PARAM_DESCRIPTION } from "./get_case_fields.js";
|
|
4
4
|
const parameters = {
|
|
5
5
|
section_id: z.number().describe("The ID of the section where the case should be created. Use get_sections to find available sections"),
|
|
@@ -12,6 +12,7 @@ The create operation requires knowing valid field names that are returned by get
|
|
|
12
12
|
`;
|
|
13
13
|
export const addCaseTool = {
|
|
14
14
|
name: "add_case",
|
|
15
|
+
mode: "write",
|
|
15
16
|
description: description.trim(),
|
|
16
17
|
parameters,
|
|
17
18
|
handler: async ({ section_id, title, fields }, client) => {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { processCustomFields } from "
|
|
3
|
-
import { LabelSchema } from "../types
|
|
2
|
+
import { processCustomFields } from "../../utils/mapper.js";
|
|
3
|
+
import { LabelSchema } from "../commons/types.js";
|
|
4
4
|
const parameters = {
|
|
5
5
|
case_id: z.string().describe("The ID of the test case (e.g. '123' or 'C123')"),
|
|
6
6
|
};
|
|
7
7
|
export const getCaseTool = {
|
|
8
8
|
name: "get_case",
|
|
9
|
+
mode: "read",
|
|
9
10
|
description: "Get detailed information about a test case including its custom fields",
|
|
10
11
|
parameters,
|
|
11
12
|
handler: async ({ case_id }, client) => {
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { parseDropdownOptions } from "
|
|
3
|
-
import { isActive } from "
|
|
2
|
+
import { parseDropdownOptions } from "../../utils/mapper.js";
|
|
3
|
+
import { isActive } from "../../utils/sanitizer.js";
|
|
4
4
|
// Field remains optional for backward compatibility with previous implementation.
|
|
5
5
|
// However, it is recommended to always provide project_id to get fields applicable to the project.
|
|
6
6
|
const parameters = {
|
|
7
7
|
project_id: z.number().optional().describe("The project ID to get fields for. This is the primary way to use this tool, as it returns only the fields relevant to your current project. If omitted, returns all fields across all projects."),
|
|
8
8
|
};
|
|
9
9
|
export const CASE_FIELDS_PARAM_DESCRIPTION = `
|
|
10
|
+
A flat key-value map of case fields. The server merges all entries directly into the root of the TestRail API request body — there is no nested "fields" key in the API call.
|
|
10
11
|
Must use system_name values from get_case_fields.
|
|
12
|
+
Example: {"priority_id": 2, "template_id": 1, "labels": [1, 2]}
|
|
11
13
|
Call get_case_fields with project_id first if field names are not already known.
|
|
12
14
|
Using an unknown field name (e.g. 'label_ids') will result in an error.
|
|
13
|
-
Example: {"priority_id": 2, "template_id": 1, "labels": [1, 2]}
|
|
14
15
|
`;
|
|
15
16
|
const FIELD_TYPE_NAMES = {
|
|
16
17
|
1: "String",
|
|
@@ -85,6 +86,7 @@ Returns available fields with their types and options (for dropdown fields).
|
|
|
85
86
|
`;
|
|
86
87
|
export const getCaseFieldsTool = {
|
|
87
88
|
name: "get_case_fields",
|
|
89
|
+
mode: "read",
|
|
88
90
|
description: description.trim(),
|
|
89
91
|
parameters,
|
|
90
92
|
handler: async ({ project_id }, client) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import fs from "fs";
|
|
3
|
-
import { validateCaseFields } from "
|
|
3
|
+
import { validateCaseFields } from "../../utils/validator.js";
|
|
4
4
|
const parameters = {
|
|
5
5
|
project_id: z.number().describe("The ID of the project. Use get_projects to find available projects"),
|
|
6
6
|
section: z.object({
|
|
@@ -15,6 +15,7 @@ const parameters = {
|
|
|
15
15
|
};
|
|
16
16
|
export const getCasesTool = {
|
|
17
17
|
name: "get_cases",
|
|
18
|
+
mode: "read",
|
|
18
19
|
description: "Get all test cases for a project. Filter by section, API params (priority, type), or any field including custom fields via 'where'. Returns case IDs, titles, and any additional requested fields.",
|
|
19
20
|
parameters,
|
|
20
21
|
handler: async ({ project_id, section, filter, where, fields, output_file }, client) => {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getCaseTool } from "./get_case.js";
|
|
2
|
+
import { getCasesTool } from "./get_cases.js";
|
|
3
|
+
import { getCaseFieldsTool } from "./get_case_fields.js";
|
|
4
|
+
import { updateCaseTool } from "./update_case.js";
|
|
5
|
+
import { updateCasesTool } from "./update_cases.js";
|
|
6
|
+
import { addCaseTool } from "./add_case.js";
|
|
7
|
+
export const casesTools = [
|
|
8
|
+
getCaseTool,
|
|
9
|
+
getCasesTool,
|
|
10
|
+
getCaseFieldsTool,
|
|
11
|
+
updateCaseTool,
|
|
12
|
+
updateCasesTool,
|
|
13
|
+
addCaseTool,
|
|
14
|
+
];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { LabelSchema } from "../commons/types.js";
|
|
3
|
+
export const CaseFieldConfigSchema = z.object({
|
|
4
|
+
context: z.object({
|
|
5
|
+
is_global: z.boolean(),
|
|
6
|
+
project_ids: z.array(z.number()),
|
|
7
|
+
}).optional(),
|
|
8
|
+
options: z.object({
|
|
9
|
+
default_value: z.string().optional(),
|
|
10
|
+
is_required: z.boolean().optional(),
|
|
11
|
+
items: z.string().optional(),
|
|
12
|
+
}),
|
|
13
|
+
});
|
|
14
|
+
export const CaseFieldSchema = z.object({
|
|
15
|
+
id: z.number(),
|
|
16
|
+
name: z.string(),
|
|
17
|
+
system_name: z.string(),
|
|
18
|
+
label: z.string(),
|
|
19
|
+
type_id: z.number(),
|
|
20
|
+
template_ids: z.array(z.number()),
|
|
21
|
+
is_active: z.boolean(),
|
|
22
|
+
description: z.string().nullable(),
|
|
23
|
+
include_all: z.boolean(),
|
|
24
|
+
configs: z.array(CaseFieldConfigSchema),
|
|
25
|
+
});
|
|
26
|
+
export const CaseSchema = z.object({
|
|
27
|
+
id: z.number(),
|
|
28
|
+
title: z.string(),
|
|
29
|
+
section_id: z.number(),
|
|
30
|
+
template_id: z.number(),
|
|
31
|
+
type_id: z.number(),
|
|
32
|
+
priority_id: z.number(),
|
|
33
|
+
milestone_id: z.number().nullable(),
|
|
34
|
+
refs: z.string().nullable(),
|
|
35
|
+
created_on: z.number(),
|
|
36
|
+
updated_on: z.number(),
|
|
37
|
+
estimate: z.string().nullable(),
|
|
38
|
+
suite_id: z.number(),
|
|
39
|
+
labels: z.array(LabelSchema),
|
|
40
|
+
}).loose();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { validateCaseFields } from "
|
|
2
|
+
import { validateCaseFields } from "../../utils/validator.js";
|
|
3
3
|
import { CASE_FIELDS_PARAM_DESCRIPTION } from "./get_case_fields.js";
|
|
4
4
|
const parameters = {
|
|
5
5
|
case_id: z.string().describe("The ID of the test case to update (e.g. '123' or 'C123')"),
|
|
@@ -12,6 +12,7 @@ Supports partial updates — only specify the fields you want to change.
|
|
|
12
12
|
`;
|
|
13
13
|
export const updateCaseTool = {
|
|
14
14
|
name: "update_case",
|
|
15
|
+
mode: "write",
|
|
15
16
|
description: description.trim(),
|
|
16
17
|
parameters,
|
|
17
18
|
handler: async ({ case_id, fields }, client) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { validateCaseFields } from "
|
|
2
|
+
import { validateCaseFields } from "../../utils/validator.js";
|
|
3
3
|
import { CASE_FIELDS_PARAM_DESCRIPTION } from "./get_case_fields.js";
|
|
4
4
|
const parameters = {
|
|
5
5
|
case_ids: z.array(z.number()).min(1).describe("Array of case IDs to update (e.g. [123, 456, 789])"),
|
|
@@ -12,6 +12,7 @@ More efficient than calling update_case multiple times.
|
|
|
12
12
|
`;
|
|
13
13
|
export const updateCasesTool = {
|
|
14
14
|
name: "update_cases",
|
|
15
|
+
mode: "write",
|
|
15
16
|
description: description.trim(),
|
|
16
17
|
parameters,
|
|
17
18
|
handler: async ({ case_ids, fields }, client) => {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const parameters = {
|
|
3
|
+
entity_type: z.enum(["case", "shared_step"]).describe("The type of the entity to delete"),
|
|
4
|
+
entity_id: z.number().describe("The ID of the entity to delete"),
|
|
5
|
+
};
|
|
6
|
+
export const deleteEntityTool = {
|
|
7
|
+
name: "delete_entity",
|
|
8
|
+
mode: "delete",
|
|
9
|
+
description: "Deletes a specified TestRail entity (case or shared_step) by its ID.",
|
|
10
|
+
parameters,
|
|
11
|
+
handler: async (args, client) => {
|
|
12
|
+
const { entity_type, entity_id } = args;
|
|
13
|
+
switch (entity_type) {
|
|
14
|
+
case "case":
|
|
15
|
+
await client.deleteCase(entity_id);
|
|
16
|
+
return { message: `Case ${entity_id} deleted successfully.` };
|
|
17
|
+
case "shared_step":
|
|
18
|
+
await client.deleteSharedStep(entity_id);
|
|
19
|
+
return { message: `Shared step ${entity_id} deleted successfully.` };
|
|
20
|
+
default:
|
|
21
|
+
throw new Error(`Unsupported entity type: ${entity_type}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ConfigurationGroupSchema } from "./types.js";
|
|
3
|
+
const parameters = {
|
|
4
|
+
project_id: z.number().int().describe("The ID of the project"),
|
|
5
|
+
};
|
|
6
|
+
export const getConfigurationsTool = {
|
|
7
|
+
name: "get_configurations",
|
|
8
|
+
mode: "read",
|
|
9
|
+
description: "Get all configuration groups and configurations for a project in TestRail.",
|
|
10
|
+
parameters,
|
|
11
|
+
handler: async (args, client) => {
|
|
12
|
+
const configs = await client.getConfigs(args.project_id);
|
|
13
|
+
return {
|
|
14
|
+
configurations: configs.map(c => ConfigurationGroupSchema.parse(c)),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { LabelSchema } from "
|
|
2
|
+
import { LabelSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
project_id: z.number().describe("The ID of the project to retrieve labels for"),
|
|
5
5
|
};
|
|
6
6
|
export const getLabelsTool = {
|
|
7
7
|
name: "get_labels",
|
|
8
|
+
mode: "read",
|
|
8
9
|
description: "Get all available test case labels (sometimes called tags) for a project. Returns label IDs and titles that can be used when creating or updating test cases.",
|
|
9
10
|
parameters,
|
|
10
11
|
handler: async ({ project_id }, client) => {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { PrioritySchema } from "
|
|
1
|
+
import { PrioritySchema } from "./types.js";
|
|
2
2
|
const parameters = {};
|
|
3
3
|
export const getPrioritiesTool = {
|
|
4
4
|
name: "get_priorities",
|
|
5
|
+
mode: "read",
|
|
5
6
|
description: "Get all available test case priorities (e.g. Critical, High, Medium, Low). Returns priority IDs and names that can be used when creating or updating test cases.",
|
|
6
7
|
parameters,
|
|
7
8
|
handler: async (_args, client) => {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { StatusSchema } from "
|
|
1
|
+
import { StatusSchema } from "./types.js";
|
|
2
2
|
const parameters = {};
|
|
3
3
|
export const getStatusesTool = {
|
|
4
4
|
name: "get_statuses",
|
|
5
|
+
mode: "read",
|
|
5
6
|
description: "Get all available test statuses (e.g. Passed, Failed, Blocked). Returns status IDs and names that can be used with add_result and get_tests",
|
|
6
7
|
parameters,
|
|
7
8
|
handler: async (args, client) => {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { TemplateSchema } from "
|
|
2
|
+
import { TemplateSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
project_id: z.number().describe("The ID of the project. Use get_projects to find available projects"),
|
|
5
5
|
};
|
|
6
6
|
export const getTemplatesTool = {
|
|
7
7
|
name: "get_templates",
|
|
8
|
+
mode: "read",
|
|
8
9
|
description: "Get available test case templates for a project. Template IDs determine which fields are available when creating or updating test cases",
|
|
9
10
|
parameters,
|
|
10
11
|
handler: async ({ project_id }, client) => {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { TestSchema } from "
|
|
2
|
+
import { TestSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
run_id: z.number().describe("The ID of the test run"),
|
|
5
5
|
status_id: z.array(z.number()).optional().describe("Optional array of status IDs to filter by. Use get_statuses to retrieve available status IDs"),
|
|
6
6
|
};
|
|
7
7
|
export const getTestsTool = {
|
|
8
8
|
name: "get_tests",
|
|
9
|
+
mode: "read",
|
|
9
10
|
description: "Get tests for a test run, optionally filtered by status",
|
|
10
11
|
parameters,
|
|
11
12
|
handler: async ({ run_id, status_id }, client) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { UserSchema } from "
|
|
2
|
+
import { UserSchema } from "./types.js";
|
|
3
3
|
const parameters = {
|
|
4
4
|
project_id: z.number().optional().describe("Optional ID of the project to retrieve users for. If omitted, attempts to fetch all users globally."),
|
|
5
5
|
fallback_all_projects: z.boolean().optional().default(true).describe("If true and global user retrieval is forbidden (non-admin), automatically falls back to fetching users across all active projects."),
|
|
@@ -21,6 +21,7 @@ async function getUsersFallbackAllProjects(client) {
|
|
|
21
21
|
}
|
|
22
22
|
export const getUsersTool = {
|
|
23
23
|
name: "get_users",
|
|
24
|
+
mode: "read",
|
|
24
25
|
description: "Get active users from TestRail. Resolves active users globally or per-project. If global fetch is forbidden (for non-admin accounts), falls back to merging users across all active projects so users referenced as reviewers or assignees can be resolved.",
|
|
25
26
|
parameters,
|
|
26
27
|
handler: async ({ project_id, fallback_all_projects }, client) => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { deleteEntityTool } from "./delete_entity.js";
|
|
2
|
+
import { getConfigurationsTool } from "./get_configurations.js";
|
|
3
|
+
import { getLabelsTool } from "./get_labels.js";
|
|
4
|
+
import { getPrioritiesTool } from "./get_priorities.js";
|
|
5
|
+
import { getStatusesTool } from "./get_statuses.js";
|
|
6
|
+
import { getTemplatesTool } from "./get_templates.js";
|
|
7
|
+
import { getTestsTool } from "./get_tests.js";
|
|
8
|
+
import { getUsersTool } from "./get_users.js";
|
|
9
|
+
export const commonsTools = [
|
|
10
|
+
deleteEntityTool,
|
|
11
|
+
getConfigurationsTool,
|
|
12
|
+
getLabelsTool,
|
|
13
|
+
getPrioritiesTool,
|
|
14
|
+
getStatusesTool,
|
|
15
|
+
getTemplatesTool,
|
|
16
|
+
getTestsTool,
|
|
17
|
+
getUsersTool,
|
|
18
|
+
];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const LabelSchema = z.object({
|
|
3
|
+
id: z.number(),
|
|
4
|
+
title: z.string(),
|
|
5
|
+
});
|
|
6
|
+
export const PrioritySchema = z.object({
|
|
7
|
+
id: z.number(),
|
|
8
|
+
is_default: z.boolean(),
|
|
9
|
+
name: z.string(),
|
|
10
|
+
});
|
|
11
|
+
export const CaseTypeSchema = z.object({
|
|
12
|
+
id: z.number(),
|
|
13
|
+
is_default: z.boolean(),
|
|
14
|
+
name: z.string(),
|
|
15
|
+
});
|
|
16
|
+
export const TemplateSchema = z.object({
|
|
17
|
+
id: z.number(),
|
|
18
|
+
name: z.string(),
|
|
19
|
+
is_default: z.boolean(),
|
|
20
|
+
});
|
|
21
|
+
export const StatusSchema = z.object({
|
|
22
|
+
id: z.number(),
|
|
23
|
+
name: z.string(),
|
|
24
|
+
label: z.string(),
|
|
25
|
+
});
|
|
26
|
+
export const TestSchema = z.object({
|
|
27
|
+
id: z.number(),
|
|
28
|
+
case_id: z.number(),
|
|
29
|
+
status_id: z.number(),
|
|
30
|
+
title: z.string(),
|
|
31
|
+
run_id: z.number(),
|
|
32
|
+
});
|
|
33
|
+
export const TestsSchema = z.array(TestSchema);
|
|
34
|
+
export const UserSchema = z.object({
|
|
35
|
+
id: z.number(),
|
|
36
|
+
name: z.string(),
|
|
37
|
+
email: z.string(),
|
|
38
|
+
});
|
|
39
|
+
export const ConfigurationSchema = z.object({
|
|
40
|
+
id: z.number(),
|
|
41
|
+
name: z.string(),
|
|
42
|
+
group_id: z.number(),
|
|
43
|
+
});
|
|
44
|
+
export const ConfigurationGroupSchema = z.object({
|
|
45
|
+
id: z.number(),
|
|
46
|
+
name: z.string(),
|
|
47
|
+
project_id: z.number(),
|
|
48
|
+
configs: z.array(ConfigurationSchema),
|
|
49
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ProjectSchema, GetOneProjectSchema, GetManyProjectsSchema } from "./types.js";
|
|
3
|
+
import { handleQuery } from "../../utils/query_handler.js";
|
|
4
|
+
const parameters = {
|
|
5
|
+
payload: z.discriminatedUnion("action", [
|
|
6
|
+
GetOneProjectSchema,
|
|
7
|
+
GetManyProjectsSchema,
|
|
8
|
+
]).describe("The payload containing the action ('one' or 'many') and corresponding parameters"),
|
|
9
|
+
};
|
|
10
|
+
export const queryProjectTool = {
|
|
11
|
+
name: "query_project",
|
|
12
|
+
mode: "read",
|
|
13
|
+
description: "Retrieve a single project or all projects in TestRail. Set payload.action to 'one' or 'many' to specify the operation.",
|
|
14
|
+
parameters,
|
|
15
|
+
handler: async (args, client) => {
|
|
16
|
+
return handleQuery(args.payload, async (p) => {
|
|
17
|
+
const project = await client.getProject(p.project_id);
|
|
18
|
+
return {
|
|
19
|
+
project: ProjectSchema.parse(project),
|
|
20
|
+
};
|
|
21
|
+
}, async (p) => {
|
|
22
|
+
const projects = await client.getProjects();
|
|
23
|
+
const filteredProjects = projects.filter(prj => !prj.is_completed);
|
|
24
|
+
return {
|
|
25
|
+
projects: filteredProjects.map(prj => ProjectSchema.parse(prj)),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const ProjectSchema = z.object({
|
|
3
|
+
id: z.number(),
|
|
4
|
+
name: z.string(),
|
|
5
|
+
is_completed: z.boolean(),
|
|
6
|
+
suite_mode: z.number(),
|
|
7
|
+
});
|
|
8
|
+
export const GetOneProjectSchema = z.object({
|
|
9
|
+
action: z.literal("one").describe("Retrieve a single project by ID"),
|
|
10
|
+
project_id: z.number().int().describe("The ID of the project"),
|
|
11
|
+
});
|
|
12
|
+
export const GetManyProjectsSchema = z.object({
|
|
13
|
+
action: z.literal("many").describe("Retrieve all available projects"),
|
|
14
|
+
});
|