@testomatio/mcp 1.0.4 → 1.0.6
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 +73 -5
- package/index.js +82 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,19 +112,80 @@ Then add this to your Cursor MCP settings:
|
|
|
112
112
|
#### Tests
|
|
113
113
|
* `get_tests` – Get all tests (params: `plan`, `query`, `state`, `suite_id`, `tag`, `labels`) — api: GET `/tests`
|
|
114
114
|
* `search_tests` – Search tests (params: `query`, `tql`, `labels`, `state`, `priority`, `filter`, `page`) — api: GET `/tests`
|
|
115
|
-
* `create_test` – Create a new test (params: `suite_id`, `title`, `description`, `code`, `file`, `state`, `tags`, `jira_issues`, `assigned_to`, `labels_ids`) — api: POST `/tests`
|
|
116
|
-
* `update_test` – Update an existing test (params: `test_id`, `suite_id`, `title`, `description`, `code`, `file`, `state`, `tags`, `jira_issues`, `assigned_to`, `labels_ids`) — api: PUT `/tests/{test_id}`
|
|
115
|
+
* `create_test` – Create a new test (params: `suite_id`, `title`, `description`, `code`, `file`, `state`, `tags`, `jira_issues`, `assigned_to`, `labels_ids`, `fields`) — api: POST `/tests`
|
|
116
|
+
* `update_test` – Update an existing test (params: `test_id`, `suite_id`, `title`, `description`, `code`, `file`, `state`, `tags`, `jira_issues`, `assigned_to`, `labels_ids`, `fields`) — api: PUT `/tests/{test_id}`
|
|
117
117
|
|
|
118
118
|
#### Test Suites
|
|
119
119
|
* `search_suites` – Search suites (params: `query`, `labels`, `state`, `priority`, `page`) — api: GET `/suites`
|
|
120
120
|
* `get_root_suites` – List root-level suites (no params) — api: GET `/suites`
|
|
121
121
|
* `get_suite` – Get one suite (params: `suite_id`) — api: GET `/suites/{suite_id}`
|
|
122
|
-
* `create_suite` – Create a new suite (params: `title`, `description`, `parent_id`) — api: POST `/suites`
|
|
123
|
-
* `create_folder` – Create a new folder (params: `title`, `description`, `parent_id`) — api: POST `/suites`
|
|
122
|
+
* `create_suite` – Create a new suite (params: `title`, `description`, `parent_id`, `fields`) — api: POST `/suites`
|
|
123
|
+
* `create_folder` – Create a new folder (params: `title`, `description`, `parent_id`, `fields`) — api: POST `/suites`
|
|
124
124
|
|
|
125
125
|
#### Labels
|
|
126
126
|
* `create_label` – Create a new label with optional custom field (params: `title`, `color`, `scope`, `visibility`, `field`) — api: POST `/labels`
|
|
127
127
|
|
|
128
|
+
### Custom Fields and Labels
|
|
129
|
+
|
|
130
|
+
The MCP server provides two distinct ways to assign values to tests, suites, and folders:
|
|
131
|
+
|
|
132
|
+
#### 1. Using `labels_ids` with label:value syntax
|
|
133
|
+
```javascript
|
|
134
|
+
{
|
|
135
|
+
"labels_ids": ["priority:high", "severity:critical", "type:regression"]
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
- Direct label assignment with values using `label:value` format
|
|
139
|
+
- Good for simple label assignments
|
|
140
|
+
- Works with existing Testomatio labels
|
|
141
|
+
|
|
142
|
+
#### 2. Using `fields` parameter (structured custom fields)
|
|
143
|
+
```javascript
|
|
144
|
+
{
|
|
145
|
+
"fields": {
|
|
146
|
+
"priority": "high",
|
|
147
|
+
"severity": "critical",
|
|
148
|
+
"risk_score": "8.5",
|
|
149
|
+
"team": "backend"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
- Structured way to set custom fields
|
|
154
|
+
- Cleaner syntax for AI assistants
|
|
155
|
+
- Supports any custom field defined in your Testomatio project
|
|
156
|
+
- Maps to Testomatio's custom-fields API
|
|
157
|
+
|
|
158
|
+
**Available for:**
|
|
159
|
+
- `create_test` and `update_test` - Test custom fields
|
|
160
|
+
- `create_suite` - Suite custom fields
|
|
161
|
+
- `create_folder` - Folder custom fields
|
|
162
|
+
|
|
163
|
+
#### Example Usage
|
|
164
|
+
```javascript
|
|
165
|
+
// Create a test with custom fields
|
|
166
|
+
{
|
|
167
|
+
"tool": "create_test",
|
|
168
|
+
"arguments": {
|
|
169
|
+
"suite_id": "123",
|
|
170
|
+
"title": "Login Test",
|
|
171
|
+
"fields": {
|
|
172
|
+
"priority": "high",
|
|
173
|
+
"severity": "critical",
|
|
174
|
+
"team": "backend"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Update a test with label:value syntax
|
|
180
|
+
{
|
|
181
|
+
"tool": "update_test",
|
|
182
|
+
"arguments": {
|
|
183
|
+
"test_id": "456",
|
|
184
|
+
"labels_ids": ["priority:high", "severity:critical"]
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
128
189
|
#### Test Runs
|
|
129
190
|
* `get_runs` – List all runs (no params) — api: GET `/runs`
|
|
130
191
|
* `get_run` – Get one run (params: `run_id`, `tree`) — api: GET `/runs/{run_id}`
|
|
@@ -146,8 +207,11 @@ Once configured, you can ask your AI assistant questions like:
|
|
|
146
207
|
- "Get all test plans for this project"
|
|
147
208
|
- "Create a new test called 'Login validation' in suite suite-123"
|
|
148
209
|
- "Update test test-456 to change its description and add @regression tag"
|
|
210
|
+
- "Create a test with custom fields: priority='high', severity='critical', team='backend'"
|
|
211
|
+
- "Update test test-789 to set custom fields for risk score and assigned team"
|
|
149
212
|
- "Create a new suite called 'Authentication Tests' with description 'All login and signup related tests'"
|
|
150
|
-
- "Create a
|
|
213
|
+
- "Create a suite with custom fields for team ownership and priority level"
|
|
214
|
+
- "Create a folder called 'API Tests' to organize API-related test suites with custom fields"
|
|
151
215
|
- "Create a label called 'Severity' with color '#ffe9ad' and predefined values like 'Blocker', 'Critical', 'Major', 'Minor', 'Normal', 'Trivial'"
|
|
152
216
|
|
|
153
217
|
## Query Patterns
|
|
@@ -168,6 +232,8 @@ These queries allow creating and updating tests:
|
|
|
168
232
|
- **"Create a new test called 'Login validation' in suite suite-123"** → `create_test` tool with `title: "Login validation"`, `suite_id: "suite-123"`
|
|
169
233
|
- **"Update test test-456 to change its description"** → `update_test` tool with `test_id: "test-456"`, `description: "new description"`
|
|
170
234
|
- **"Create an automated test with @smoke tag"** → `create_test` tool with `state: "automated"`, `tags: ["smoke"]`
|
|
235
|
+
- **"Create a test with custom fields: priority='high', severity='critical'"** → `create_test` tool with `title: "Test Title"`, `suite_id: "suite-123"`, `fields: { "priority": "high", "severity": "critical" }`
|
|
236
|
+
- **"Update test test-789 to set custom fields for risk score and team"** → `update_test` tool with `test_id: "test-789"`, `fields: { "risk_score": "8.5", "team": "backend" }`
|
|
171
237
|
|
|
172
238
|
### Suite and Folder Management Queries
|
|
173
239
|
|
|
@@ -175,7 +241,9 @@ These queries help organize your test structure:
|
|
|
175
241
|
|
|
176
242
|
- **"Create a new suite called 'Authentication Tests'"** → `create_suite` tool with `title: "Authentication Tests"`
|
|
177
243
|
- **"Create a suite for login tests with description"** → `create_suite` tool with `title: "Login Tests"`, `description: "All login related test cases"`
|
|
244
|
+
- **"Create a suite with custom fields for team ownership and priority level"** → `create_suite` tool with `title: "Backend Tests"`, `fields: { "team": "backend", "priority": "high" }`
|
|
178
245
|
- **"Create a folder called 'API Tests' under parent suite-123"** → `create_folder` tool with `title: "API Tests"`, `parent_id: "suite-123"`
|
|
246
|
+
- **"Create a folder with custom fields for team and project"** → `create_folder` tool with `title: "Integration Tests"`, `fields: { "team": "qa", "project": "mobile-app" }`
|
|
179
247
|
- **"Create a test suite for payment features"** → `create_suite` tool with `title: "Payment Features", description: "Tests covering payment processing"`
|
|
180
248
|
- **"Create a folder to organize integration tests"** → `create_folder` tool with `title: "Integration Tests"`
|
|
181
249
|
|
package/index.js
CHANGED
|
@@ -8,9 +8,29 @@ import {
|
|
|
8
8
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
9
9
|
import { program } from 'commander';
|
|
10
10
|
|
|
11
|
+
function normalizeString(value) {
|
|
12
|
+
return typeof value === 'string' ? value.trim() : value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function normalizeBaseUrl(value) {
|
|
16
|
+
if (typeof value !== 'string') {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const trimmed = value.trim();
|
|
21
|
+
// Remove any internal whitespace characters that may appear when the URL
|
|
22
|
+
// gets broken across lines (e.g. "http://\n localhost:3000").
|
|
23
|
+
return trimmed.replace(/\s+/g, '');
|
|
24
|
+
}
|
|
25
|
+
|
|
11
26
|
class TestomatioMCPServer {
|
|
12
27
|
constructor(config) {
|
|
13
|
-
this.config =
|
|
28
|
+
this.config = {
|
|
29
|
+
...config,
|
|
30
|
+
token: normalizeString(config.token),
|
|
31
|
+
projectId: normalizeString(config.projectId),
|
|
32
|
+
baseUrl: normalizeBaseUrl(config.baseUrl),
|
|
33
|
+
};
|
|
14
34
|
this.jwtToken = null;
|
|
15
35
|
this.server = new Server(
|
|
16
36
|
{
|
|
@@ -318,7 +338,14 @@ class TestomatioMCPServer {
|
|
|
318
338
|
labels_ids: {
|
|
319
339
|
type: 'array',
|
|
320
340
|
items: { type: 'string' },
|
|
321
|
-
description: 'Slugs of labels to assign to the test',
|
|
341
|
+
description: 'Slugs of labels to assign to the test. Supports label:value format (e.g., ["priority:high", "severity:critical"])',
|
|
342
|
+
},
|
|
343
|
+
fields: {
|
|
344
|
+
type: 'object',
|
|
345
|
+
description: 'Set custom fields for the test. Object with field names as keys and values as properties (e.g., {"priority": "high", "severity": "critical"})',
|
|
346
|
+
additionalProperties: {
|
|
347
|
+
type: 'string'
|
|
348
|
+
},
|
|
322
349
|
},
|
|
323
350
|
},
|
|
324
351
|
required: ['suite_id', 'title'],
|
|
@@ -381,7 +408,14 @@ class TestomatioMCPServer {
|
|
|
381
408
|
labels_ids: {
|
|
382
409
|
type: 'array',
|
|
383
410
|
items: { type: 'string' },
|
|
384
|
-
description: 'Slugs of labels to assign to the test',
|
|
411
|
+
description: 'Slugs of labels to assign to the test. Supports label:value format (e.g., ["priority:high", "severity:critical"])',
|
|
412
|
+
},
|
|
413
|
+
fields: {
|
|
414
|
+
type: 'object',
|
|
415
|
+
description: 'Set custom fields for the test. Object with field names as keys and values as properties (e.g., {"priority": "high", "severity": "critical"})',
|
|
416
|
+
additionalProperties: {
|
|
417
|
+
type: 'string'
|
|
418
|
+
},
|
|
385
419
|
},
|
|
386
420
|
},
|
|
387
421
|
required: ['test_id'],
|
|
@@ -405,6 +439,13 @@ class TestomatioMCPServer {
|
|
|
405
439
|
type: 'string',
|
|
406
440
|
description: 'Parent suite ID to create this suite under',
|
|
407
441
|
},
|
|
442
|
+
fields: {
|
|
443
|
+
type: 'object',
|
|
444
|
+
description: 'Set custom fields for the suite. Object with field names as keys and values as properties (e.g., {"priority": "high", "team": "backend"})',
|
|
445
|
+
additionalProperties: {
|
|
446
|
+
type: 'string'
|
|
447
|
+
},
|
|
448
|
+
},
|
|
408
449
|
},
|
|
409
450
|
required: ['title'],
|
|
410
451
|
},
|
|
@@ -427,6 +468,13 @@ class TestomatioMCPServer {
|
|
|
427
468
|
type: 'string',
|
|
428
469
|
description: 'Parent folder or suite ID to create this folder under',
|
|
429
470
|
},
|
|
471
|
+
fields: {
|
|
472
|
+
type: 'object',
|
|
473
|
+
description: 'Set custom fields for the folder. Object with field names as keys and values as properties (e.g., {"priority": "high", "team": "backend"})',
|
|
474
|
+
additionalProperties: {
|
|
475
|
+
type: 'string'
|
|
476
|
+
},
|
|
477
|
+
},
|
|
430
478
|
},
|
|
431
479
|
required: ['title'],
|
|
432
480
|
},
|
|
@@ -1022,14 +1070,19 @@ class TestomatioMCPServer {
|
|
|
1022
1070
|
}
|
|
1023
1071
|
|
|
1024
1072
|
async createTest(args) {
|
|
1025
|
-
const { suite_id, labels_ids, ...attributes } = args;
|
|
1073
|
+
const { suite_id, labels_ids, fields, ...attributes } = args;
|
|
1074
|
+
|
|
1075
|
+
// Handle fields parameter for custom fields
|
|
1026
1076
|
const requestData = {
|
|
1027
1077
|
data: {
|
|
1028
1078
|
type: 'tests',
|
|
1029
|
-
attributes:
|
|
1079
|
+
attributes: {
|
|
1080
|
+
...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
|
|
1081
|
+
...(fields && { 'custom-fields': fields })
|
|
1082
|
+
}
|
|
1030
1083
|
}
|
|
1031
1084
|
};
|
|
1032
|
-
|
|
1085
|
+
|
|
1033
1086
|
if (suite_id) {
|
|
1034
1087
|
requestData.data.relationships = {
|
|
1035
1088
|
suite: {
|
|
@@ -1040,7 +1093,7 @@ class TestomatioMCPServer {
|
|
|
1040
1093
|
}
|
|
1041
1094
|
};
|
|
1042
1095
|
}
|
|
1043
|
-
|
|
1096
|
+
|
|
1044
1097
|
if (labels_ids) requestData.labels_ids = labels_ids;
|
|
1045
1098
|
|
|
1046
1099
|
const data = await this.makePostRequest('/tests', requestData);
|
|
@@ -1060,14 +1113,19 @@ class TestomatioMCPServer {
|
|
|
1060
1113
|
}
|
|
1061
1114
|
|
|
1062
1115
|
async updateTest(args) {
|
|
1063
|
-
const { test_id, suite_id, labels_ids, ...attributes } = args;
|
|
1116
|
+
const { test_id, suite_id, labels_ids, fields, ...attributes } = args;
|
|
1117
|
+
|
|
1118
|
+
// Handle fields parameter for custom fields
|
|
1064
1119
|
const requestData = {
|
|
1065
1120
|
data: {
|
|
1066
1121
|
type: 'tests',
|
|
1067
|
-
attributes:
|
|
1122
|
+
attributes: {
|
|
1123
|
+
...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
|
|
1124
|
+
...(fields && { 'custom-fields': fields })
|
|
1125
|
+
}
|
|
1068
1126
|
}
|
|
1069
1127
|
};
|
|
1070
|
-
|
|
1128
|
+
|
|
1071
1129
|
if (suite_id) {
|
|
1072
1130
|
requestData.data.relationships = {
|
|
1073
1131
|
suite: {
|
|
@@ -1078,7 +1136,7 @@ class TestomatioMCPServer {
|
|
|
1078
1136
|
}
|
|
1079
1137
|
};
|
|
1080
1138
|
}
|
|
1081
|
-
|
|
1139
|
+
|
|
1082
1140
|
if (labels_ids) requestData.labels_ids = labels_ids;
|
|
1083
1141
|
|
|
1084
1142
|
const data = await this.makePutRequest(`/tests/${test_id}`, requestData);
|
|
@@ -1098,13 +1156,14 @@ class TestomatioMCPServer {
|
|
|
1098
1156
|
}
|
|
1099
1157
|
|
|
1100
1158
|
async createSuite(args) {
|
|
1101
|
-
const { parent_id, ...attributes } = args;
|
|
1159
|
+
const { parent_id, fields, ...attributes } = args;
|
|
1102
1160
|
const requestData = {
|
|
1103
1161
|
data: {
|
|
1104
1162
|
type: 'suites',
|
|
1105
1163
|
attributes: {
|
|
1106
1164
|
...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
|
|
1107
|
-
'file-type': 'file'
|
|
1165
|
+
'file-type': 'file',
|
|
1166
|
+
...(fields && { 'custom-fields': fields })
|
|
1108
1167
|
}
|
|
1109
1168
|
}
|
|
1110
1169
|
};
|
|
@@ -1136,13 +1195,14 @@ class TestomatioMCPServer {
|
|
|
1136
1195
|
}
|
|
1137
1196
|
|
|
1138
1197
|
async createFolder(args) {
|
|
1139
|
-
const { parent_id, ...attributes } = args;
|
|
1198
|
+
const { parent_id, fields, ...attributes } = args;
|
|
1140
1199
|
const requestData = {
|
|
1141
1200
|
data: {
|
|
1142
1201
|
type: 'suites',
|
|
1143
1202
|
attributes: {
|
|
1144
1203
|
...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
|
|
1145
|
-
'file-type': 'folder'
|
|
1204
|
+
'file-type': 'folder',
|
|
1205
|
+
...(fields && { 'custom-fields': fields })
|
|
1146
1206
|
}
|
|
1147
1207
|
}
|
|
1148
1208
|
};
|
|
@@ -1245,9 +1305,13 @@ function parseArgs() {
|
|
|
1245
1305
|
|
|
1246
1306
|
const options = program.opts();
|
|
1247
1307
|
|
|
1248
|
-
const token = options.token || process.env.TESTOMATIO_API_TOKEN;
|
|
1249
|
-
const projectId = options.project || process.env.TESTOMATIO_PROJECT_ID;
|
|
1250
|
-
const baseUrl =
|
|
1308
|
+
const token = normalizeString(options.token || process.env.TESTOMATIO_API_TOKEN);
|
|
1309
|
+
const projectId = normalizeString(options.project || process.env.TESTOMATIO_PROJECT_ID);
|
|
1310
|
+
const baseUrl = normalizeBaseUrl(
|
|
1311
|
+
options.baseUrl ||
|
|
1312
|
+
process.env.TESTOMATIO_BASE_URL ||
|
|
1313
|
+
'https://app.testomat.io'
|
|
1314
|
+
);
|
|
1251
1315
|
|
|
1252
1316
|
if (!token) {
|
|
1253
1317
|
console.error('Error: API token is required. Use --token <token> or set TESTOMATIO_API_TOKEN environment variable');
|