@uarlouski/testrail-mcp-server 1.3.1 → 1.5.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 +178 -48
- package/dist/client/testrail.js +40 -10
- package/dist/index.js +1 -1
- package/dist/tools/add_attachment_to_run.js +2 -2
- package/dist/tools/add_run.js +3 -3
- package/dist/tools/create_case.js +1 -1
- package/dist/tools/get_case.js +5 -3
- package/dist/tools/get_cases.js +14 -4
- package/dist/tools/get_sections.js +1 -1
- package/dist/tools/get_templates.js +1 -1
- package/dist/tools/update_case.js +2 -1
- package/dist/tools/update_cases.js +2 -2
- package/dist/types/testrail.js +4 -1
- package/dist/utils/sanitizer.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,41 +1,103 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">TestRail MCP Server</h1>
|
|
3
|
+
<p align="center">
|
|
4
|
+
A Model Context Protocol (MCP) server that connects AI assistants to TestRail test management — search, create, update, and execute test cases using natural language.
|
|
5
|
+
</p>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://www.npmjs.com/package/@uarlouski/testrail-mcp-server"><img src="https://badge.fury.io/js/@uarlouski%2Ftestrail-mcp-server.svg" alt="npm version"></a>
|
|
10
|
+
<a href="https://www.npmjs.com/package/@uarlouski/testrail-mcp-server"><img src="https://img.shields.io/npm/dm/@uarlouski/testrail-mcp-server.svg" alt="npm downloads"></a>
|
|
11
|
+
<a href="https://github.com/uarlouski/testrail-mcp-server/actions/workflows/ci.yml"><img src="https://github.com/uarlouski/testrail-mcp-server/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
12
|
+
<a href="https://opensource.org/licenses/Apache-2.0"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License"></a>
|
|
13
|
+
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-5.9-blue.svg" alt="TypeScript"></a>
|
|
14
|
+
<a href="https://github.com/uarlouski/testrail-mcp-server"><img src="https://img.shields.io/github/stars/uarlouski/testrail-mcp-server.svg?style=social&label=Star" alt="GitHub stars"></a>
|
|
15
|
+
</p>
|
|
2
16
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Why TestRail MCP Server?
|
|
20
|
+
|
|
21
|
+
Managing test cases manually is tedious. With the TestRail MCP Server, your AI assistant (Claude, Cursor, Windsurf, or any MCP-compliant client) talks directly to TestRail. Ask it to find test cases, create new ones, kick off test runs, or report results — all through natural conversation.
|
|
22
|
+
|
|
23
|
+
**No context switching. No copy-pasting. Just ask.**
|
|
24
|
+
|
|
25
|
+
## ✨ Features
|
|
9
26
|
|
|
27
|
+
| Category | Capabilities |
|
|
28
|
+
|----------|-------------|
|
|
29
|
+
| **🔍 Discovery** | Browse projects, suites, and sections to map your test organization |
|
|
30
|
+
| **📋 Case Management** | Fetch, create, update, and bulk-edit test cases with full custom field support |
|
|
31
|
+
| **▶️ Test Execution** | Create runs, record results, attach files, and track test statuses |
|
|
32
|
+
| **🧠 Context-Aware** | Exposes templates, field definitions, priorities, and case types so LLMs generate valid, well-structured test cases |
|
|
10
33
|
|
|
11
|
-
|
|
34
|
+
## 🚀 Quick Start
|
|
12
35
|
|
|
13
|
-
|
|
36
|
+
### 1. Get Your TestRail API Key
|
|
14
37
|
|
|
15
|
-
|
|
38
|
+
Navigate to **My Settings → API Keys** in your TestRail instance and generate a new key.
|
|
16
39
|
|
|
17
|
-
|
|
18
|
-
- **Smart Discovery**: List projects, suites, and sections to help LLMs understand your test organization.
|
|
19
|
-
- **Comprehensive Case Operations**: Fetch, create, and update test cases with full support for custom fields.
|
|
20
|
-
- **Context-Aware**: Designed to provide LLMs with the right context (templates, field options) for accurate test generation.
|
|
40
|
+
### 2. Configure Your MCP Client
|
|
21
41
|
|
|
22
|
-
|
|
42
|
+
Add the server to your MCP client configuration. Below are examples for popular clients.
|
|
43
|
+
|
|
44
|
+
<details>
|
|
45
|
+
<summary><strong>Claude Desktop</strong></summary>
|
|
46
|
+
|
|
47
|
+
Edit your `claude_desktop_config.json`:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"mcpServers": {
|
|
52
|
+
"testrail": {
|
|
53
|
+
"command": "npx",
|
|
54
|
+
"args": ["-y", "@uarlouski/testrail-mcp-server@latest"],
|
|
55
|
+
"env": {
|
|
56
|
+
"TESTRAIL_INSTANCE_URL": "https://your-instance.testrail.io",
|
|
57
|
+
"TESTRAIL_USERNAME": "your@email.com",
|
|
58
|
+
"TESTRAIL_API_KEY": "your-api-key"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
</details>
|
|
66
|
+
|
|
67
|
+
<details>
|
|
68
|
+
<summary><strong>Cursor</strong></summary>
|
|
69
|
+
|
|
70
|
+
Open **Settings → MCP** and add a new server:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"testrail": {
|
|
76
|
+
"command": "npx",
|
|
77
|
+
"args": ["-y", "@uarlouski/testrail-mcp-server@latest"],
|
|
78
|
+
"env": {
|
|
79
|
+
"TESTRAIL_INSTANCE_URL": "https://your-instance.testrail.io",
|
|
80
|
+
"TESTRAIL_USERNAME": "your@email.com",
|
|
81
|
+
"TESTRAIL_API_KEY": "your-api-key"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
23
87
|
|
|
24
|
-
|
|
88
|
+
</details>
|
|
25
89
|
|
|
26
|
-
|
|
90
|
+
<details>
|
|
91
|
+
<summary><strong>Windsurf</strong></summary>
|
|
27
92
|
|
|
28
|
-
Add
|
|
93
|
+
Add to your Windsurf MCP configuration:
|
|
29
94
|
|
|
30
95
|
```json
|
|
31
96
|
{
|
|
32
97
|
"mcpServers": {
|
|
33
98
|
"testrail": {
|
|
34
99
|
"command": "npx",
|
|
35
|
-
"args": [
|
|
36
|
-
"-y",
|
|
37
|
-
"@uarlouski/testrail-mcp-server@latest"
|
|
38
|
-
],
|
|
100
|
+
"args": ["-y", "@uarlouski/testrail-mcp-server@latest"],
|
|
39
101
|
"env": {
|
|
40
102
|
"TESTRAIL_INSTANCE_URL": "https://your-instance.testrail.io",
|
|
41
103
|
"TESTRAIL_USERNAME": "your@email.com",
|
|
@@ -46,42 +108,110 @@ Add the following to your configuration file:
|
|
|
46
108
|
}
|
|
47
109
|
```
|
|
48
110
|
|
|
49
|
-
|
|
111
|
+
</details>
|
|
112
|
+
|
|
113
|
+
<details>
|
|
114
|
+
<summary><strong>Other MCP Clients</strong></summary>
|
|
115
|
+
|
|
116
|
+
Any MCP-compliant client can use this server. The configuration pattern is the same — point it at the `npx` command with the required environment variables.
|
|
117
|
+
|
|
118
|
+
</details>
|
|
119
|
+
|
|
120
|
+
### 3. Start Using It
|
|
121
|
+
|
|
122
|
+
Once configured, ask your AI assistant things like:
|
|
123
|
+
|
|
124
|
+
- *"List all projects in TestRail"*
|
|
125
|
+
- *"Show me the test cases in suite 1 of project 3"*
|
|
126
|
+
- *"Create a test case for login validation with steps"*
|
|
127
|
+
- *"Start a new test run with cases from section 5"*
|
|
128
|
+
- *"Mark test 42 as passed with a comment"*
|
|
129
|
+
|
|
130
|
+
## ⚙️ Configuration
|
|
50
131
|
|
|
51
|
-
| Variable | Description | Required |
|
|
52
|
-
|
|
53
|
-
| `TESTRAIL_INSTANCE_URL` |
|
|
54
|
-
| `TESTRAIL_USERNAME` | Your TestRail email address |
|
|
55
|
-
| `TESTRAIL_API_KEY` | Your TestRail API key (
|
|
132
|
+
| Environment Variable | Description | Required |
|
|
133
|
+
|---------------------|-------------|:--------:|
|
|
134
|
+
| `TESTRAIL_INSTANCE_URL` | Your TestRail instance URL (e.g., `https://example.testrail.io`) | ✅ |
|
|
135
|
+
| `TESTRAIL_USERNAME` | Your TestRail email address | ✅ |
|
|
136
|
+
| `TESTRAIL_API_KEY` | Your TestRail API key ([how to get one](https://support.testrail.com/hc/en-us/articles/7077039051412-Accessing-the-TestRail-API)) | ✅ |
|
|
56
137
|
|
|
57
138
|
## 🛠️ Available Tools
|
|
58
139
|
|
|
59
|
-
|
|
140
|
+
### Discovery
|
|
60
141
|
|
|
61
142
|
| Tool | Description |
|
|
62
143
|
|------|-------------|
|
|
63
|
-
| `get_projects` |
|
|
64
|
-
| `
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
|
69
|
-
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
144
|
+
| `get_projects` | List all available projects in your TestRail instance |
|
|
145
|
+
| `get_sections` | Navigate the folder/section hierarchy of a test suite |
|
|
146
|
+
|
|
147
|
+
### Test Case Management
|
|
148
|
+
|
|
149
|
+
| Tool | Description |
|
|
150
|
+
|------|-------------|
|
|
151
|
+
| `get_cases` | Query test cases with filtering by priority, template, type, and more — supports pagination and recursive section fetching |
|
|
152
|
+
| `get_case` | Fetch complete details of a specific test case including steps and custom fields |
|
|
153
|
+
| `create_case` | Create a new test case with full custom field support |
|
|
154
|
+
| `update_case` | Modify an existing test case's fields, title, or steps |
|
|
155
|
+
| `update_cases` | Bulk-update multiple test cases simultaneously |
|
|
156
|
+
|
|
157
|
+
### Test Execution
|
|
158
|
+
|
|
159
|
+
| Tool | Description |
|
|
160
|
+
|------|-------------|
|
|
161
|
+
| `add_run` | Create a new test run in TestRail |
|
|
162
|
+
| `get_tests` | Get tests for a run, optionally filtered by status |
|
|
163
|
+
| `add_results` | Submit one or more test results to a run |
|
|
164
|
+
| `add_attachment_to_run` | Attach a file or zipped folder to a test run |
|
|
165
|
+
|
|
166
|
+
### Metadata
|
|
167
|
+
|
|
168
|
+
| Tool | Description |
|
|
169
|
+
|------|-------------|
|
|
170
|
+
| `get_statuses` | List all available test statuses (Passed, Failed, Blocked, etc.) |
|
|
171
|
+
| `get_case_fields` | Retrieve custom field definitions and dropdown options |
|
|
172
|
+
| `get_templates` | List available case templates for correct case structure |
|
|
173
|
+
|
|
174
|
+
## 🏗️ Development
|
|
175
|
+
|
|
176
|
+
### Prerequisites
|
|
177
|
+
|
|
178
|
+
- [Node.js](https://nodejs.org/) 18+
|
|
179
|
+
- npm
|
|
180
|
+
|
|
181
|
+
### Setup
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
git clone https://github.com/uarlouski/testrail-mcp-server.git
|
|
185
|
+
cd testrail-mcp-server
|
|
186
|
+
npm install
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Build
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npm run build
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Run Tests
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
npm test
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 🤝 Contributing
|
|
202
|
+
|
|
203
|
+
Contributions are welcome! Please feel free to open an [issue](https://github.com/uarlouski/testrail-mcp-server/issues) or submit a pull request.
|
|
80
204
|
|
|
81
205
|
## 📜 License
|
|
82
206
|
|
|
83
|
-
This project is licensed under the Apache 2.0
|
|
207
|
+
This project is licensed under the [Apache License 2.0](LICENSE).
|
|
84
208
|
|
|
85
209
|
---
|
|
86
210
|
|
|
87
|
-
|
|
211
|
+
<p align="center">
|
|
212
|
+
<b>TestRail MCP Server</b> · Built with the <a href="https://modelcontextprotocol.io">Model Context Protocol</a>
|
|
213
|
+
</p>
|
|
214
|
+
|
|
215
|
+
<p align="center">
|
|
216
|
+
<sub>Keywords: TestRail MCP Server, Model Context Protocol, TestRail API integration, AI test management, LLM testing tools, Claude TestRail, Cursor TestRail, Windsurf TestRail, test case automation, QA AI assistant, MCP server for testing, TestRail plugin, test run management, AI-powered QA</sub>
|
|
217
|
+
</p>
|
package/dist/client/testrail.js
CHANGED
|
@@ -23,6 +23,23 @@ export class TestRailClient {
|
|
|
23
23
|
async getCase(caseId) {
|
|
24
24
|
return this.get(`${API_BASE_V2}/get_case/${caseId}`);
|
|
25
25
|
}
|
|
26
|
+
async getCasesRecursively(projectId, sectionId, filter, excludedSectionNames) {
|
|
27
|
+
const sections = await this.getSections(projectId);
|
|
28
|
+
const sectionIds = this.getSubSectionIds(sections, sectionId, excludedSectionNames);
|
|
29
|
+
sectionIds.push(sectionId);
|
|
30
|
+
const promises = sectionIds.map(id => this.getCases(projectId, id, filter));
|
|
31
|
+
const results = await Promise.all(promises);
|
|
32
|
+
return results.flat();
|
|
33
|
+
}
|
|
34
|
+
getSubSectionIds(sections, parentId, excludedSectionNames) {
|
|
35
|
+
const children = sections.filter(s => s.parent_id === parentId);
|
|
36
|
+
const validChildren = children.filter(s => !excludedSectionNames?.includes(s.name));
|
|
37
|
+
let ids = validChildren.map(s => s.id);
|
|
38
|
+
for (const child of validChildren) {
|
|
39
|
+
ids = ids.concat(this.getSubSectionIds(sections, child.id, excludedSectionNames));
|
|
40
|
+
}
|
|
41
|
+
return ids;
|
|
42
|
+
}
|
|
26
43
|
async getCases(projectId, sectionId, filter) {
|
|
27
44
|
let url = `${API_BASE_V2}/get_cases/${projectId}`;
|
|
28
45
|
if (sectionId) {
|
|
@@ -73,10 +90,10 @@ export class TestRailClient {
|
|
|
73
90
|
return this.statusesPromise;
|
|
74
91
|
}
|
|
75
92
|
async getTemplates(projectId) {
|
|
76
|
-
if (!this.templatesPromiseMap.has(projectId)) {
|
|
77
|
-
this.templatesPromiseMap.set(projectId, this.get(`${API_BASE_V2}/get_templates/${projectId}`));
|
|
93
|
+
if (!this.templatesPromiseMap.has(projectId.toString())) {
|
|
94
|
+
this.templatesPromiseMap.set(projectId.toString(), this.get(`${API_BASE_V2}/get_templates/${projectId}`));
|
|
78
95
|
}
|
|
79
|
-
return this.templatesPromiseMap.get(projectId);
|
|
96
|
+
return this.templatesPromiseMap.get(projectId.toString());
|
|
80
97
|
}
|
|
81
98
|
async updateCase(caseId, fields) {
|
|
82
99
|
return this.post(`${API_BASE_V2}/update_case/${caseId}`, fields);
|
|
@@ -155,13 +172,26 @@ export class TestRailClient {
|
|
|
155
172
|
headers,
|
|
156
173
|
body,
|
|
157
174
|
};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
175
|
+
console.error(`[TestRailClient] Executing ${method} request to ${endpoint}`);
|
|
176
|
+
const startTime = Date.now();
|
|
177
|
+
try {
|
|
178
|
+
const response = await fetch(url, params);
|
|
179
|
+
const duration = Date.now() - startTime;
|
|
180
|
+
console.error(`[TestRailClient] Received ${response.status} ${response.statusText} from ${method} ${endpoint} in ${duration}ms`);
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
let errorMessage = `TestRail API Error: ${response.status} ${response.statusText}`;
|
|
183
|
+
const errorText = await response.text();
|
|
184
|
+
errorMessage += ` - ${errorText}`;
|
|
185
|
+
console.error(`[TestRailClient] Error Details: ${errorMessage}`);
|
|
186
|
+
throw new Error(errorMessage);
|
|
187
|
+
}
|
|
188
|
+
return await response.json();
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (error instanceof Error && !error.message.startsWith('TestRail API Error')) {
|
|
192
|
+
console.error(`[TestRailClient] Request Failed: ${method} ${endpoint} - ${error.message}`);
|
|
193
|
+
}
|
|
194
|
+
throw error;
|
|
164
195
|
}
|
|
165
|
-
return await response.json();
|
|
166
196
|
}
|
|
167
197
|
}
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ if (!TESTRAIL_INSTANCE_URL || !TESTRAIL_USERNAME || !TESTRAIL_API_KEY) {
|
|
|
27
27
|
}
|
|
28
28
|
const server = new McpServer({
|
|
29
29
|
name: "TestRail MCP Server",
|
|
30
|
-
version: "1.
|
|
30
|
+
version: "1.5.0",
|
|
31
31
|
});
|
|
32
32
|
const client = new TestRailClient(TESTRAIL_INSTANCE_URL, TESTRAIL_USERNAME, TESTRAIL_API_KEY);
|
|
33
33
|
const tools = [
|
|
@@ -5,7 +5,7 @@ import * as path from "path";
|
|
|
5
5
|
import * as os from "os";
|
|
6
6
|
import archiver from "archiver";
|
|
7
7
|
const parameters = {
|
|
8
|
-
run_id: z.
|
|
8
|
+
run_id: z.number().describe("The ID of the test run to attach the file to"),
|
|
9
9
|
file_path: z.string().describe("The path to the file or directory to attach. Directories will be automatically zipped."),
|
|
10
10
|
};
|
|
11
11
|
export const addAttachmentToRunTool = {
|
|
@@ -40,7 +40,7 @@ export const addAttachmentToRunTool = {
|
|
|
40
40
|
isTemporary = true;
|
|
41
41
|
}
|
|
42
42
|
try {
|
|
43
|
-
const result = await client.addAttachmentToRun(
|
|
43
|
+
const result = await client.addAttachmentToRun(run_id, uploadPath, filename);
|
|
44
44
|
return AttachmentSchema.parse(result);
|
|
45
45
|
}
|
|
46
46
|
finally {
|
package/dist/tools/add_run.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { RunSchema } from "../types/testrail.js";
|
|
3
3
|
const parameters = {
|
|
4
|
-
project_id: z.
|
|
4
|
+
project_id: z.number().describe("The ID of the project. Use get_projects to find available projects"),
|
|
5
5
|
name: z.string().optional().describe("The name of the test run"),
|
|
6
6
|
description: z.string().optional().describe("The description of the test run"),
|
|
7
|
-
case_ids: z.array(z.number()).describe("Array of case IDs to include in the run. After creating the run, use get_tests with the returned run_id to retrieve test IDs for result submission")
|
|
7
|
+
case_ids: z.array(z.number()).min(1).describe("Array of case IDs to include in the run. After creating the run, use get_tests with the returned run_id to retrieve test IDs for result submission")
|
|
8
8
|
};
|
|
9
9
|
export const addRunTool = {
|
|
10
10
|
name: "add_run",
|
|
11
11
|
description: "Create a new test run in TestRail",
|
|
12
12
|
parameters,
|
|
13
13
|
handler: async ({ project_id, ...fields }, client) => {
|
|
14
|
-
const caseData = await client.getCase(
|
|
14
|
+
const caseData = await client.getCase(fields.case_ids[0]);
|
|
15
15
|
const params = { suite_id: caseData.suite_id, include_all: false, ...fields };
|
|
16
16
|
const run = await client.addRun(project_id, params);
|
|
17
17
|
return RunSchema.parse(run);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
const parameters = {
|
|
3
|
-
section_id: z.
|
|
3
|
+
section_id: z.number().describe("The ID of the section where the case should be created. Use get_sections to find available sections"),
|
|
4
4
|
title: z.string().describe("The title of the test case"),
|
|
5
5
|
fields: z.record(z.string(), z.any()).optional().describe("Optional fields for the test case. First use get_templates to discover available templates, then get_case_fields to see available fields for your template. Example: {\"priority_id\": 2, \"template_id\": 1, \"custom_automation_priority\": 1}"),
|
|
6
6
|
};
|
package/dist/tools/get_case.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { processCustomFields } from "../utils/mapper.js";
|
|
3
|
+
import { LabelSchema } from "../types/testrail.js";
|
|
3
4
|
const parameters = {
|
|
4
5
|
case_id: z.string().describe("The ID of the test case (e.g. '123' or 'C123')"),
|
|
5
6
|
};
|
|
@@ -8,10 +9,11 @@ export const getCaseTool = {
|
|
|
8
9
|
description: "Get detailed information about a test case including its custom fields",
|
|
9
10
|
parameters,
|
|
10
11
|
handler: async ({ case_id }, client) => {
|
|
11
|
-
const
|
|
12
|
+
const idString = case_id.toUpperCase().startsWith("C") ? case_id.substring(1) : case_id;
|
|
13
|
+
const id = Number(idString);
|
|
12
14
|
const testCase = await client.getCase(id);
|
|
13
15
|
const [section, caseTypes, priorities, caseFields] = await Promise.all([
|
|
14
|
-
client.getSection(testCase.section_id
|
|
16
|
+
client.getSection(Number(testCase.section_id)),
|
|
15
17
|
client.getCaseTypes(),
|
|
16
18
|
client.getPriorities(),
|
|
17
19
|
client.getCaseFields()
|
|
@@ -25,7 +27,7 @@ export const getCaseTool = {
|
|
|
25
27
|
section: section.name,
|
|
26
28
|
type: caseType ? caseType.name : "Unknown",
|
|
27
29
|
priority: priority ? priority.name : "Unknown",
|
|
28
|
-
labels: testCase.labels || [],
|
|
30
|
+
labels: (testCase.labels || []).map((label) => LabelSchema.parse(label)),
|
|
29
31
|
references: testCase.refs,
|
|
30
32
|
updated_on: testCase.updated_on,
|
|
31
33
|
...customFields
|
package/dist/tools/get_cases.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
const parameters = {
|
|
3
|
-
project_id: z.
|
|
4
|
-
|
|
3
|
+
project_id: z.number().describe("The ID of the project. Use get_projects to find available projects"),
|
|
4
|
+
section: z.object({
|
|
5
|
+
id: z.number(),
|
|
6
|
+
recursive: z.boolean().optional().default(false).describe("If true, fetches cases from the section and all its child sections"),
|
|
7
|
+
excludes: z.array(z.string()).optional().describe("List of section names to exclude from the recursive search"),
|
|
8
|
+
}).optional().describe("Section filter configuration. Use get_sections to find available sections"),
|
|
5
9
|
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\"}"),
|
|
6
10
|
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}"),
|
|
7
11
|
fields: z.array(z.string()).optional().describe("Additional fields to include in response beyond id, title, and suite_id. Use get_case_fields to see available fields. Example: [\"priority_id\", \"type_id\", \"custom_automation_status\"]"),
|
|
@@ -10,8 +14,14 @@ export const getCasesTool = {
|
|
|
10
14
|
name: "get_cases",
|
|
11
15
|
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.",
|
|
12
16
|
parameters,
|
|
13
|
-
handler: async ({ project_id,
|
|
14
|
-
let cases
|
|
17
|
+
handler: async ({ project_id, section, filter, where, fields }, client) => {
|
|
18
|
+
let cases;
|
|
19
|
+
if (section?.recursive) {
|
|
20
|
+
cases = await client.getCasesRecursively(project_id, section.id, filter, section.excludes);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
cases = await client.getCases(project_id, section?.id, filter);
|
|
24
|
+
}
|
|
15
25
|
// Client-side filtering for custom fields and other unsupported API filters
|
|
16
26
|
if (where) {
|
|
17
27
|
cases = cases.filter(c => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { SectionSchema } from "../types/testrail.js";
|
|
3
3
|
const parameters = {
|
|
4
|
-
project_id: z.
|
|
4
|
+
project_id: z.number().describe("The ID of the project. Use get_projects to find available projects"),
|
|
5
5
|
};
|
|
6
6
|
export const getSectionsTool = {
|
|
7
7
|
name: "get_sections",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { TemplateSchema } from "../types/testrail.js";
|
|
3
3
|
const parameters = {
|
|
4
|
-
project_id: z.
|
|
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,7 +8,8 @@ export const updateCaseTool = {
|
|
|
8
8
|
description: "Update a test case in TestRail. Supports partial updates - only specify the fields you want to change",
|
|
9
9
|
parameters,
|
|
10
10
|
handler: async ({ case_id, fields }, client) => {
|
|
11
|
-
const
|
|
11
|
+
const idString = case_id.toUpperCase().startsWith("C") ? case_id.substring(1) : case_id;
|
|
12
|
+
const id = Number(idString);
|
|
12
13
|
const updatedCase = await client.updateCase(id, fields);
|
|
13
14
|
return {
|
|
14
15
|
success: true,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
const parameters = {
|
|
3
|
-
case_ids: z.array(z.number()).describe("Array of case IDs to update (e.g. [123, 456, 789])"),
|
|
3
|
+
case_ids: z.array(z.number()).min(1).describe("Array of case IDs to update (e.g. [123, 456, 789])"),
|
|
4
4
|
fields: z.record(z.string(), z.any()).describe("Object containing fields to update for ALL specified cases. Use get_case_fields to see available fields. Example: {\"priority_id\": 2, \"type_id\": 1}"),
|
|
5
5
|
};
|
|
6
6
|
export const updateCasesTool = {
|
|
@@ -8,7 +8,7 @@ export const updateCasesTool = {
|
|
|
8
8
|
description: "Bulk update multiple test cases with the same field values. More efficient than calling update_case multiple times",
|
|
9
9
|
parameters,
|
|
10
10
|
handler: async ({ case_ids, fields }, client) => {
|
|
11
|
-
const caseData = await client.getCase(
|
|
11
|
+
const caseData = await client.getCase(case_ids[0]);
|
|
12
12
|
const updatedCases = await client.updateCases(caseData.suite_id, case_ids, fields);
|
|
13
13
|
return {
|
|
14
14
|
success: true,
|
package/dist/types/testrail.js
CHANGED
|
@@ -18,6 +18,9 @@ export const CaseFieldSchema = z.object({
|
|
|
18
18
|
include_all: z.boolean(),
|
|
19
19
|
configs: z.array(CaseFieldConfigSchema),
|
|
20
20
|
});
|
|
21
|
+
export const LabelSchema = z.object({
|
|
22
|
+
title: z.string(),
|
|
23
|
+
});
|
|
21
24
|
export const CaseSchema = z.object({
|
|
22
25
|
id: z.number(),
|
|
23
26
|
title: z.string(),
|
|
@@ -31,7 +34,7 @@ export const CaseSchema = z.object({
|
|
|
31
34
|
updated_on: z.number(),
|
|
32
35
|
estimate: z.string().nullable(),
|
|
33
36
|
suite_id: z.number(),
|
|
34
|
-
labels: z.array(
|
|
37
|
+
labels: z.array(LabelSchema),
|
|
35
38
|
}).loose();
|
|
36
39
|
export const SectionSchema = z.object({
|
|
37
40
|
id: z.number(),
|
package/dist/utils/sanitizer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Recursively
|
|
2
|
+
* Recursively strips inline style attributes from HTML tags contained in string values
|
|
3
3
|
* in an object or array. Useful for cleaning up TestRail markdown content before sending
|
|
4
4
|
* to LLMs.
|
|
5
5
|
*/
|
package/package.json
CHANGED