@testomatio/mcp 1.0.9 → 1.0.12
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 +30 -12
- package/index.js +195 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A Model Context Protocol (MCP) server for Testomat.io API integration with AI assistants like Cursor.
|
|
4
4
|
|
|
5
|
+
<img width="1920" height="1085" alt="image" src="https://github.com/user-attachments/assets/cf823e8b-1305-4ed2-a7c5-712efec12ceb" />
|
|
6
|
+
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
7
9
|
### Prerequisites
|
|
@@ -62,6 +64,17 @@ Your project ID can be found in the URL when you're viewing your project:
|
|
|
62
64
|
https://app.testomat.io/projects/YOUR_PROJECT_ID
|
|
63
65
|
```
|
|
64
66
|
|
|
67
|
+
### ID Formats
|
|
68
|
+
|
|
69
|
+
Testomatio uses specific ID formats for different resources:
|
|
70
|
+
|
|
71
|
+
- **Test IDs**: Start with `T` prefix followed by 7 alphanumeric characters (e.g., `Ta1b2c3d4`)
|
|
72
|
+
- **Suite IDs**: Start with `S` prefix followed by 7 alphanumeric characters (e.g., `Sx9y8z7w6`)
|
|
73
|
+
- **Total length**: 8 characters including the prefix
|
|
74
|
+
- **Format**: `[Prefix][7 alphanumeric characters]`
|
|
75
|
+
|
|
76
|
+
When working with tools that require specific IDs, ensure you use the correct format with the appropriate prefix.
|
|
77
|
+
|
|
65
78
|
## Integration with Cursor
|
|
66
79
|
|
|
67
80
|
To use this MCP server with Cursor, add the following configuration to your Cursor settings:
|
|
@@ -111,6 +124,7 @@ Then add this to your Cursor MCP settings:
|
|
|
111
124
|
|
|
112
125
|
#### Tests
|
|
113
126
|
* `get_tests` – Get all tests (params: `plan`, `query`, `state`, `suite_id`, `tag`, `labels`) — api: GET `/tests`
|
|
127
|
+
* `get_test` – Get a specific test by ID with all information including labels, tags, and metadata (params: `test_id`) — api: GET `/tests/{test_id}`
|
|
114
128
|
* `search_tests` – Search tests (params: `query`, `tql`, `labels`, `state`, `priority`, `filter`, `page`) — api: GET `/tests`
|
|
115
129
|
* `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
130
|
* `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}`
|
|
@@ -123,7 +137,9 @@ Then add this to your Cursor MCP settings:
|
|
|
123
137
|
* `create_folder` – Create a new folder (params: `title`, `description`, `parent_id`, `fields`) — api: POST `/suites`
|
|
124
138
|
|
|
125
139
|
#### Labels
|
|
126
|
-
* `
|
|
140
|
+
* `get_labels` – Get all available labels with their IDs and configurations
|
|
141
|
+
* `create_label` – Create a new label with optional custom field
|
|
142
|
+
* `unlink_label` – Remove a label from a test or suite
|
|
127
143
|
|
|
128
144
|
### Custom Fields and Labels
|
|
129
145
|
|
|
@@ -200,15 +216,16 @@ The MCP server provides two distinct ways to assign values to tests, suites, and
|
|
|
200
216
|
Once configured, you can ask your AI assistant questions like:
|
|
201
217
|
|
|
202
218
|
- "Show me all the tests in the project"
|
|
203
|
-
- "Get
|
|
219
|
+
- "Get details for test ID Ta1b2c3d4"
|
|
220
|
+
- "Get the test runs for test ID Ta1b2c3d4"
|
|
204
221
|
- "What are the root suites in this project?"
|
|
205
222
|
- "Show me details for test run xyz789"
|
|
206
223
|
- "List all automated tests with the @smoke tag"
|
|
207
224
|
- "Get all test plans for this project"
|
|
208
|
-
- "Create a new test called 'Login validation' in suite
|
|
209
|
-
- "Update test
|
|
225
|
+
- "Create a new test called 'Login validation' in suite Sx9y8z7w6"
|
|
226
|
+
- "Update test Ta1b2c3d4 to change its description and add @regression tag"
|
|
210
227
|
- "Create a test with custom fields: priority='high', severity='critical', team='backend'"
|
|
211
|
-
- "Update test
|
|
228
|
+
- "Update test Tb2c3d4e5 to set custom fields for risk score and assigned team"
|
|
212
229
|
- "Create a new suite called 'Authentication Tests' with description 'All login and signup related tests'"
|
|
213
230
|
- "Create a suite with custom fields for team ownership and priority level"
|
|
214
231
|
- "Create a folder called 'API Tests' to organize API-related test suites with custom fields"
|
|
@@ -229,11 +246,11 @@ These queries retrieve general information without specific filtering:
|
|
|
229
246
|
|
|
230
247
|
These queries allow creating and updating tests:
|
|
231
248
|
|
|
232
|
-
- **"Create a new test called 'Login validation' in suite
|
|
233
|
-
- **"Update test
|
|
249
|
+
- **"Create a new test called 'Login validation' in suite Sx9y8z7w6"** → `create_test` tool with `title: "Login validation"`, `suite_id: "Sx9y8z7w6"`
|
|
250
|
+
- **"Update test Ta1b2c3d4 to change its description"** → `update_test` tool with `test_id: "Ta1b2c3d4"`, `description: "new description"`
|
|
234
251
|
- **"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: "
|
|
236
|
-
- **"Update test
|
|
252
|
+
- **"Create a test with custom fields: priority='high', severity='critical'"** → `create_test` tool with `title: "Test Title"`, `suite_id: "Sx9y8z7w6"`, `fields: { "priority": "high", "severity": "critical" }`
|
|
253
|
+
- **"Update test Tb2c3d4e5 to set custom fields for risk score and team"** → `update_test` tool with `test_id: "Tb2c3d4e5"`, `fields: { "risk_score": "8.5", "team": "backend" }`
|
|
237
254
|
|
|
238
255
|
### Suite and Folder Management Queries
|
|
239
256
|
|
|
@@ -242,7 +259,7 @@ These queries help organize your test structure:
|
|
|
242
259
|
- **"Create a new suite called 'Authentication Tests'"** → `create_suite` tool with `title: "Authentication Tests"`
|
|
243
260
|
- **"Create a suite for login tests with description"** → `create_suite` tool with `title: "Login Tests"`, `description: "All login related test cases"`
|
|
244
261
|
- **"Create a suite with custom fields for team ownership and priority level"** → `create_suite` tool with `title: "Backend Tests"`, `fields: { "team": "backend", "priority": "high" }`
|
|
245
|
-
- **"Create a folder called 'API Tests' under parent suite
|
|
262
|
+
- **"Create a folder called 'API Tests' under parent suite Sx9y8z7w6"** → `create_folder` tool with `title: "API Tests"`, `parent_id: "Sx9y8z7w6"`
|
|
246
263
|
- **"Create a folder with custom fields for team and project"** → `create_folder` tool with `title: "Integration Tests"`, `fields: { "team": "qa", "project": "mobile-app" }`
|
|
247
264
|
- **"Create a test suite for payment features"** → `create_suite` tool with `title: "Payment Features", description: "Tests covering payment processing"`
|
|
248
265
|
- **"Create a folder to organize integration tests"** → `create_folder` tool with `title: "Integration Tests"`
|
|
@@ -262,9 +279,10 @@ These queries help create custom labels for better test categorization:
|
|
|
262
279
|
|
|
263
280
|
These queries target specific entities by ID:
|
|
264
281
|
|
|
265
|
-
- **"Get
|
|
282
|
+
- **"Get details for test ID Ta1b2c3d4"** → `get_test` tool with `test_id: "Ta1b2c3d4"`
|
|
283
|
+
- **"Get test runs for test ID Ta1b2c3d4"** → `get_testruns` tool with `test_id: "Ta1b2c3d4"`
|
|
266
284
|
- **"Show me details for test run xyz789"** → `get_run` tool with `run_id: "xyz789"`
|
|
267
|
-
- **"Get suite details for suite
|
|
285
|
+
- **"Get suite details for suite Sx9y8z7w6"** → `get_suite` tool with `suite_id: "Sx9y8z7w6"`
|
|
268
286
|
|
|
269
287
|
### Search and Filter Queries
|
|
270
288
|
|
package/index.js
CHANGED
|
@@ -116,6 +116,20 @@ class TestomatioMCPServer {
|
|
|
116
116
|
},
|
|
117
117
|
},
|
|
118
118
|
},
|
|
119
|
+
{
|
|
120
|
+
name: 'get_test',
|
|
121
|
+
description: 'Get a specific test by its ID with all information including labels, tags, and metadata',
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
test_id: {
|
|
126
|
+
type: 'string',
|
|
127
|
+
description: 'The ID of the test to retrieve',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
required: ['test_id'],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
119
133
|
{
|
|
120
134
|
name: 'search_tests',
|
|
121
135
|
description: 'Search tests by keywords, tags, labels, TQL queries, and other filters',
|
|
@@ -303,7 +317,7 @@ class TestomatioMCPServer {
|
|
|
303
317
|
},
|
|
304
318
|
title: {
|
|
305
319
|
type: 'string',
|
|
306
|
-
description: 'Test title',
|
|
320
|
+
description: 'Test title. @tags in the title (e.g., "@smoke test") will be automatically extracted as tags',
|
|
307
321
|
},
|
|
308
322
|
description: {
|
|
309
323
|
type: 'string',
|
|
@@ -325,7 +339,7 @@ class TestomatioMCPServer {
|
|
|
325
339
|
tags: {
|
|
326
340
|
type: 'array',
|
|
327
341
|
items: { type: 'string' },
|
|
328
|
-
description: 'List of tags for the test',
|
|
342
|
+
description: 'List of @tags for the test. Tags are automatically extracted from @ mentions in the title (e.g., @smoke, @regression). Can also be provided as an array of tag names (without @ prefix).',
|
|
329
343
|
},
|
|
330
344
|
jira_issues: {
|
|
331
345
|
type: 'array',
|
|
@@ -368,7 +382,7 @@ class TestomatioMCPServer {
|
|
|
368
382
|
},
|
|
369
383
|
title: {
|
|
370
384
|
type: 'string',
|
|
371
|
-
description: 'Test title',
|
|
385
|
+
description: 'Test title. @tags in the title (e.g., "@smoke test") will be automatically extracted as tags',
|
|
372
386
|
},
|
|
373
387
|
description: {
|
|
374
388
|
type: 'string',
|
|
@@ -395,7 +409,7 @@ class TestomatioMCPServer {
|
|
|
395
409
|
tags: {
|
|
396
410
|
type: 'array',
|
|
397
411
|
items: { type: 'string' },
|
|
398
|
-
description: 'List of tags for the test',
|
|
412
|
+
description: 'List of @tags for the test. Tags are automatically extracted from @ mentions in the title (e.g., @smoke, @regression). Can also be provided as an array of tag names (without @ prefix).',
|
|
399
413
|
},
|
|
400
414
|
jira_issues: {
|
|
401
415
|
type: 'array',
|
|
@@ -501,6 +515,36 @@ class TestomatioMCPServer {
|
|
|
501
515
|
},
|
|
502
516
|
},
|
|
503
517
|
},
|
|
518
|
+
{
|
|
519
|
+
name: 'unlink_label',
|
|
520
|
+
description: 'Remove a label from a test or suite. Can remove specific label values or all instances of the label',
|
|
521
|
+
inputSchema: {
|
|
522
|
+
type: 'object',
|
|
523
|
+
properties: {
|
|
524
|
+
label_id: {
|
|
525
|
+
type: 'string',
|
|
526
|
+
description: 'Label ID to remove (e.g., "priority", "severity")',
|
|
527
|
+
},
|
|
528
|
+
test_id: {
|
|
529
|
+
type: 'string',
|
|
530
|
+
description: 'Test ID to remove label from (use either test_id or suite_id)',
|
|
531
|
+
},
|
|
532
|
+
suite_id: {
|
|
533
|
+
type: 'string',
|
|
534
|
+
description: 'Suite ID to remove label from (use either test_id or suite_id)',
|
|
535
|
+
},
|
|
536
|
+
value: {
|
|
537
|
+
type: 'string',
|
|
538
|
+
description: 'Specific label value to remove (e.g., "high", "critical"). If omitted, all instances are removed',
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
required: ['label_id'],
|
|
542
|
+
oneOf: [
|
|
543
|
+
{ required: ['test_id'] },
|
|
544
|
+
{ required: ['suite_id'] }
|
|
545
|
+
],
|
|
546
|
+
},
|
|
547
|
+
},
|
|
504
548
|
{
|
|
505
549
|
name: 'create_label',
|
|
506
550
|
description: 'Create a new label with optional custom field configuration. Labels can be used to tag and categorize tests and suites',
|
|
@@ -564,6 +608,8 @@ class TestomatioMCPServer {
|
|
|
564
608
|
switch (name) {
|
|
565
609
|
case 'get_tests':
|
|
566
610
|
return await this.getTests(args);
|
|
611
|
+
case 'get_test':
|
|
612
|
+
return await this.getTest(args.test_id);
|
|
567
613
|
case 'search_tests':
|
|
568
614
|
return await this.searchTests(args);
|
|
569
615
|
case 'search_suites':
|
|
@@ -584,6 +630,8 @@ class TestomatioMCPServer {
|
|
|
584
630
|
return await this.getPlan(args.plan_id);
|
|
585
631
|
case 'get_labels':
|
|
586
632
|
return await this.getLabels(args);
|
|
633
|
+
case 'unlink_label':
|
|
634
|
+
return await this.unlinkLabel(args);
|
|
587
635
|
case 'create_test':
|
|
588
636
|
return await this.createTest(args);
|
|
589
637
|
case 'update_test':
|
|
@@ -719,19 +767,22 @@ class TestomatioMCPServer {
|
|
|
719
767
|
if (fieldName === 'tags') {
|
|
720
768
|
return value.map(tag => `<tag>${this.escapeXml(tag)}</tag>`).join('');
|
|
721
769
|
}
|
|
722
|
-
if (fieldName === 'labels') {
|
|
723
|
-
return value.map(label => `<label>${this.escapeXml(label)}</label>`).join('');
|
|
724
|
-
}
|
|
725
770
|
if (fieldName === 'tests-ids') {
|
|
726
771
|
return value.map(id => `<test_id>${id}</test_id>`).join('');
|
|
727
772
|
}
|
|
728
|
-
// Default array handling
|
|
729
|
-
|
|
773
|
+
// Default array handling - use field name as tag (e.g., labels -> <label>)
|
|
774
|
+
const singularFieldName = fieldName.endsWith('s') ? fieldName.slice(0, -1) : fieldName;
|
|
775
|
+
return value.map(item => {
|
|
776
|
+
if (typeof item === 'object' && item !== null) {
|
|
777
|
+
return `<${singularFieldName}>${JSON.stringify(item)}</${singularFieldName}>`;
|
|
778
|
+
}
|
|
779
|
+
return `<${singularFieldName}>${this.escapeXml(item)}</${singularFieldName}>`;
|
|
780
|
+
}).join('');
|
|
730
781
|
}
|
|
731
782
|
|
|
732
|
-
// Handle nested objects
|
|
783
|
+
// Handle nested objects - stringify them without escaping
|
|
733
784
|
if (typeof value === 'object') {
|
|
734
|
-
return
|
|
785
|
+
return JSON.stringify(value);
|
|
735
786
|
}
|
|
736
787
|
|
|
737
788
|
// Handle strings that need escaping
|
|
@@ -796,11 +847,16 @@ class TestomatioMCPServer {
|
|
|
796
847
|
|
|
797
848
|
async getTests(filters = {}) {
|
|
798
849
|
const params = this.buildSearchParams(filters);
|
|
850
|
+
// Add labels=true and detail=true for comprehensive test information
|
|
851
|
+
params.labels = 'true';
|
|
852
|
+
params.detail = 'true';
|
|
853
|
+
|
|
799
854
|
const data = await this.makeRequest('/tests', params);
|
|
800
855
|
const formattedTests = data.data.map(test =>
|
|
801
856
|
this.formatModel(test, 'test', [
|
|
802
|
-
'title', 'description', 'code', 'priority',
|
|
803
|
-
'
|
|
857
|
+
'title', 'description', 'code', 'priority', 'state',
|
|
858
|
+
'suite-id', 'tags', 'file', 'jira-issues', 'assigned-to',
|
|
859
|
+
'created-at', 'updated-at', 'labels'
|
|
804
860
|
])
|
|
805
861
|
).join('\n\n');
|
|
806
862
|
|
|
@@ -814,6 +870,25 @@ class TestomatioMCPServer {
|
|
|
814
870
|
};
|
|
815
871
|
}
|
|
816
872
|
|
|
873
|
+
async getTest(testId) {
|
|
874
|
+
// Add labels=true and detail=true for comprehensive test information
|
|
875
|
+
const data = await this.makeRequest(`/tests/${testId}`, { labels: 'true', detail: 'true' });
|
|
876
|
+
const formattedTest = this.formatModel(data.data, 'test', [
|
|
877
|
+
'title', 'description', 'code', 'priority', 'state',
|
|
878
|
+
'suite-id', 'tags', 'file', 'jira-issues', 'assigned-to',
|
|
879
|
+
'created-at', 'updated-at', 'labels'
|
|
880
|
+
]);
|
|
881
|
+
|
|
882
|
+
return {
|
|
883
|
+
content: [
|
|
884
|
+
{
|
|
885
|
+
type: 'text',
|
|
886
|
+
text: `Test ${testId}:\n\n${formattedTest}`,
|
|
887
|
+
},
|
|
888
|
+
],
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
|
|
817
892
|
buildSearchParams(filters = {}) {
|
|
818
893
|
const params = {};
|
|
819
894
|
|
|
@@ -861,12 +936,17 @@ class TestomatioMCPServer {
|
|
|
861
936
|
|
|
862
937
|
async searchTests(filters = {}) {
|
|
863
938
|
const params = this.buildSearchParams(filters);
|
|
939
|
+
// Add labels=true and detail=true for comprehensive test information
|
|
940
|
+
params.labels = 'true';
|
|
941
|
+
params.detail = 'true';
|
|
942
|
+
|
|
864
943
|
const data = await this.makeRequest('/tests', params);
|
|
865
944
|
|
|
866
945
|
const formattedTests = data.data.map(test =>
|
|
867
946
|
this.formatModel(test, 'test', [
|
|
868
|
-
'title', 'description', 'code', 'priority',
|
|
869
|
-
'
|
|
947
|
+
'title', 'description', 'code', 'priority', 'state',
|
|
948
|
+
'suite-id', 'tags', 'file', 'jira-issues', 'assigned-to',
|
|
949
|
+
'created-at', 'updated-at', 'labels'
|
|
870
950
|
])
|
|
871
951
|
).join('\n\n');
|
|
872
952
|
|
|
@@ -1130,9 +1210,56 @@ class TestomatioMCPServer {
|
|
|
1130
1210
|
};
|
|
1131
1211
|
}
|
|
1132
1212
|
|
|
1213
|
+
extractTagsFromTitle(title) {
|
|
1214
|
+
if (!title) return [];
|
|
1215
|
+
|
|
1216
|
+
// Find all @tags in the title
|
|
1217
|
+
const tagMatches = title.match(/@([a-zA-Z0-9_\-]+)/g);
|
|
1218
|
+
|
|
1219
|
+
if (!tagMatches) return [];
|
|
1220
|
+
|
|
1221
|
+
// Remove @ prefix and deduplicate
|
|
1222
|
+
const tags = tagMatches
|
|
1223
|
+
.map(tag => tag.substring(1)) // Remove @
|
|
1224
|
+
.filter(tag => tag.length > 0); // Filter out empty tags
|
|
1225
|
+
|
|
1226
|
+
return [...new Set(tags)]; // Remove duplicates
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
mergeTags(explicitTags, titleTags) {
|
|
1230
|
+
const allTags = [];
|
|
1231
|
+
|
|
1232
|
+
// Add explicit tags (remove @ prefix if present)
|
|
1233
|
+
if (explicitTags && Array.isArray(explicitTags)) {
|
|
1234
|
+
allTags.push(...explicitTags.map(tag => tag.replace(/^@/, '')));
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// Add tags extracted from title
|
|
1238
|
+
if (titleTags && Array.isArray(titleTags)) {
|
|
1239
|
+
allTags.push(...titleTags);
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// Remove duplicates while preserving order
|
|
1243
|
+
const uniqueTags = [];
|
|
1244
|
+
const seen = new Set();
|
|
1245
|
+
|
|
1246
|
+
for (const tag of allTags) {
|
|
1247
|
+
if (!seen.has(tag) && tag.length > 0) {
|
|
1248
|
+
seen.add(tag);
|
|
1249
|
+
uniqueTags.push(tag);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return uniqueTags;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1133
1256
|
async createTest(args) {
|
|
1134
1257
|
const { suite_id, labels_ids, fields, ...attributes } = args;
|
|
1135
1258
|
|
|
1259
|
+
// Extract tags from title if provided
|
|
1260
|
+
const titleTags = this.extractTagsFromTitle(attributes.title);
|
|
1261
|
+
const mergedTags = this.mergeTags(attributes.tags, titleTags);
|
|
1262
|
+
|
|
1136
1263
|
// Convert attributes to use hyphens instead of underscores for API compatibility
|
|
1137
1264
|
const apiAttributes = Object.fromEntries(
|
|
1138
1265
|
Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])
|
|
@@ -1149,6 +1276,7 @@ class TestomatioMCPServer {
|
|
|
1149
1276
|
type: 'tests',
|
|
1150
1277
|
attributes: {
|
|
1151
1278
|
...apiAttributes,
|
|
1279
|
+
...(mergedTags.length > 0 && { tags: mergedTags }),
|
|
1152
1280
|
...(labels_ids && { labels_ids: labels_ids }),
|
|
1153
1281
|
...(fields && { 'custom-fields': fields })
|
|
1154
1282
|
}
|
|
@@ -1198,9 +1326,60 @@ class TestomatioMCPServer {
|
|
|
1198
1326
|
}
|
|
1199
1327
|
}
|
|
1200
1328
|
|
|
1329
|
+
async unlinkLabel(args) {
|
|
1330
|
+
const { label_id, test_id, suite_id, value } = args;
|
|
1331
|
+
|
|
1332
|
+
// Validate that either test_id or suite_id is provided
|
|
1333
|
+
if (!test_id && !suite_id) {
|
|
1334
|
+
throw new Error('Either test_id or suite_id must be provided');
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
if (test_id && suite_id) {
|
|
1338
|
+
throw new Error('Cannot specify both test_id and suite_id. Use one or the other.');
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// Build URL with base label link endpoint
|
|
1342
|
+
let url = `/labels/${label_id}/link`;
|
|
1343
|
+
|
|
1344
|
+
// Add appropriate query parameters
|
|
1345
|
+
if (test_id) {
|
|
1346
|
+
url += `?test_id=${test_id}`;
|
|
1347
|
+
} else if (suite_id) {
|
|
1348
|
+
url += `?suite_id=${suite_id}`;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// Add event=remove parameter
|
|
1352
|
+
url += `&event=remove`;
|
|
1353
|
+
|
|
1354
|
+
// Add value as query parameter if present
|
|
1355
|
+
if (value) {
|
|
1356
|
+
url += `&value=${encodeURIComponent(value)}`;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// Make the API request
|
|
1360
|
+
await this.makePostRequest(url, {});
|
|
1361
|
+
|
|
1362
|
+
const itemType = test_id ? 'test' : 'suite';
|
|
1363
|
+
const itemId = test_id || suite_id;
|
|
1364
|
+
const removeDescription = value ? `value "${value}"` : 'all instances';
|
|
1365
|
+
|
|
1366
|
+
return {
|
|
1367
|
+
content: [
|
|
1368
|
+
{
|
|
1369
|
+
type: 'text',
|
|
1370
|
+
text: `Successfully removed label "${label_id}" (${removeDescription}) from ${itemType} "${itemId}"`,
|
|
1371
|
+
},
|
|
1372
|
+
],
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1201
1376
|
async updateTest(args) {
|
|
1202
1377
|
const { test_id, suite_id, labels_ids, fields, ...attributes } = args;
|
|
1203
1378
|
|
|
1379
|
+
// Extract tags from title if provided
|
|
1380
|
+
const titleTags = this.extractTagsFromTitle(attributes.title);
|
|
1381
|
+
const mergedTags = this.mergeTags(attributes.tags, titleTags);
|
|
1382
|
+
|
|
1204
1383
|
// Convert attributes to use hyphens instead of underscores for API compatibility
|
|
1205
1384
|
const apiAttributes = Object.fromEntries(
|
|
1206
1385
|
Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])
|
|
@@ -1220,6 +1399,7 @@ class TestomatioMCPServer {
|
|
|
1220
1399
|
type: 'tests',
|
|
1221
1400
|
attributes: {
|
|
1222
1401
|
...apiAttributes,
|
|
1402
|
+
...(mergedTags.length > 0 && { tags: mergedTags }),
|
|
1223
1403
|
...(fields && { 'custom-fields': fields })
|
|
1224
1404
|
}
|
|
1225
1405
|
}
|