@testomatio/mcp 1.0.8 → 1.0.11

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.
Files changed (3) hide show
  1. package/README.md +30 -12
  2. package/index.js +295 -33
  3. 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
- * `create_label` – Create a new label with optional custom field (params: `title`, `color`, `scope`, `visibility`, `field`) — api: POST `/labels`
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 the test runs for test ID abc123"
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 suite-123"
209
- - "Update test test-456 to change its description and add @regression tag"
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 test-789 to set custom fields for risk score and assigned team"
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 suite-123"** → `create_test` tool with `title: "Login validation"`, `suite_id: "suite-123"`
233
- - **"Update test test-456 to change its description"** → `update_test` tool with `test_id: "test-456"`, `description: "new description"`
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: "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" }`
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-123"** → `create_folder` tool with `title: "API Tests"`, `parent_id: "suite-123"`
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 test runs for test ID abc123"** → `get_testruns` tool with `test_id: "abc123"`
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-456"** → `get_suite` tool with `suite_id: "suite-456"`
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',
@@ -480,6 +494,57 @@ class TestomatioMCPServer {
480
494
  required: ['title'],
481
495
  },
482
496
  },
497
+ {
498
+ name: 'get_labels',
499
+ description: 'Get all available labels for the project with their IDs and configurations',
500
+ inputSchema: {
501
+ type: 'object',
502
+ properties: {
503
+ scope: {
504
+ type: 'array',
505
+ items: {
506
+ type: 'string',
507
+ enum: ['tests', 'suites']
508
+ },
509
+ description: 'Filter labels by scope (e.g., ["tests"], ["suites"], or ["tests", "suites"])',
510
+ },
511
+ page: {
512
+ type: 'number',
513
+ description: 'Page number for pagination',
514
+ },
515
+ },
516
+ },
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
+ },
483
548
  {
484
549
  name: 'create_label',
485
550
  description: 'Create a new label with optional custom field configuration. Labels can be used to tag and categorize tests and suites',
@@ -543,6 +608,8 @@ class TestomatioMCPServer {
543
608
  switch (name) {
544
609
  case 'get_tests':
545
610
  return await this.getTests(args);
611
+ case 'get_test':
612
+ return await this.getTest(args.test_id);
546
613
  case 'search_tests':
547
614
  return await this.searchTests(args);
548
615
  case 'search_suites':
@@ -561,6 +628,10 @@ class TestomatioMCPServer {
561
628
  return await this.getPlans(args);
562
629
  case 'get_plan':
563
630
  return await this.getPlan(args.plan_id);
631
+ case 'get_labels':
632
+ return await this.getLabels(args);
633
+ case 'unlink_label':
634
+ return await this.unlinkLabel(args);
564
635
  case 'create_test':
565
636
  return await this.createTest(args);
566
637
  case 'update_test':
@@ -773,11 +844,16 @@ class TestomatioMCPServer {
773
844
 
774
845
  async getTests(filters = {}) {
775
846
  const params = this.buildSearchParams(filters);
847
+ // Add labels=true and detail=true for comprehensive test information
848
+ params.labels = 'true';
849
+ params.detail = 'true';
850
+
776
851
  const data = await this.makeRequest('/tests', params);
777
852
  const formattedTests = data.data.map(test =>
778
853
  this.formatModel(test, 'test', [
779
- 'title', 'description', 'code', 'priority',
780
- 'state', 'suite-id', 'tags', 'file'
854
+ 'title', 'description', 'code', 'priority', 'state',
855
+ 'suite-id', 'tags', 'file', 'jira-issues', 'assigned-to',
856
+ 'created-at', 'updated-at', 'labels'
781
857
  ])
782
858
  ).join('\n\n');
783
859
 
@@ -791,6 +867,25 @@ class TestomatioMCPServer {
791
867
  };
792
868
  }
793
869
 
870
+ async getTest(testId) {
871
+ // Add labels=true and detail=true for comprehensive test information
872
+ const data = await this.makeRequest(`/tests/${testId}`, { labels: 'true', detail: 'true' });
873
+ const formattedTest = this.formatModel(data.data, 'test', [
874
+ 'title', 'description', 'code', 'priority', 'state',
875
+ 'suite-id', 'tags', 'file', 'jira-issues', 'assigned-to',
876
+ 'created-at', 'updated-at', 'labels'
877
+ ]);
878
+
879
+ return {
880
+ content: [
881
+ {
882
+ type: 'text',
883
+ text: `Test ${testId}:\n\n${formattedTest}`,
884
+ },
885
+ ],
886
+ };
887
+ }
888
+
794
889
  buildSearchParams(filters = {}) {
795
890
  const params = {};
796
891
 
@@ -838,12 +933,17 @@ class TestomatioMCPServer {
838
933
 
839
934
  async searchTests(filters = {}) {
840
935
  const params = this.buildSearchParams(filters);
936
+ // Add labels=true and detail=true for comprehensive test information
937
+ params.labels = 'true';
938
+ params.detail = 'true';
939
+
841
940
  const data = await this.makeRequest('/tests', params);
842
941
 
843
942
  const formattedTests = data.data.map(test =>
844
943
  this.formatModel(test, 'test', [
845
- 'title', 'description', 'code', 'priority',
846
- 'state', 'suite-id', 'tags', 'file'
944
+ 'title', 'description', 'code', 'priority', 'state',
945
+ 'suite-id', 'tags', 'file', 'jira-issues', 'assigned-to',
946
+ 'created-at', 'updated-at', 'labels'
847
947
  ])
848
948
  ).join('\n\n');
849
949
 
@@ -1072,9 +1172,91 @@ class TestomatioMCPServer {
1072
1172
  };
1073
1173
  }
1074
1174
 
1175
+ async getLabels(filters = {}) {
1176
+ const params = {};
1177
+
1178
+ // Handle scope filter
1179
+ if (filters.scope && Array.isArray(filters.scope)) {
1180
+ if (!params['scope[]']) {
1181
+ params['scope[]'] = [];
1182
+ }
1183
+ filters.scope.forEach(scope => {
1184
+ params['scope[]'].push(scope);
1185
+ });
1186
+ }
1187
+
1188
+ // Handle pagination
1189
+ if (filters.page) {
1190
+ params.page = filters.page;
1191
+ }
1192
+
1193
+ const data = await this.makeRequest('/labels', params);
1194
+ const formattedLabels = data.data.map(label =>
1195
+ this.formatModel(label, 'label', [
1196
+ 'title', 'color', 'scope', 'visibility', 'field'
1197
+ ])
1198
+ ).join('\n\n');
1199
+
1200
+ return {
1201
+ content: [
1202
+ {
1203
+ type: 'text',
1204
+ text: `Available labels for project ${this.config.projectId}:\n\n${formattedLabels || 'No labels found matching the criteria.'}`,
1205
+ },
1206
+ ],
1207
+ };
1208
+ }
1209
+
1210
+ extractTagsFromTitle(title) {
1211
+ if (!title) return [];
1212
+
1213
+ // Find all @tags in the title
1214
+ const tagMatches = title.match(/@([a-zA-Z0-9_\-]+)/g);
1215
+
1216
+ if (!tagMatches) return [];
1217
+
1218
+ // Remove @ prefix and deduplicate
1219
+ const tags = tagMatches
1220
+ .map(tag => tag.substring(1)) // Remove @
1221
+ .filter(tag => tag.length > 0); // Filter out empty tags
1222
+
1223
+ return [...new Set(tags)]; // Remove duplicates
1224
+ }
1225
+
1226
+ mergeTags(explicitTags, titleTags) {
1227
+ const allTags = [];
1228
+
1229
+ // Add explicit tags (remove @ prefix if present)
1230
+ if (explicitTags && Array.isArray(explicitTags)) {
1231
+ allTags.push(...explicitTags.map(tag => tag.replace(/^@/, '')));
1232
+ }
1233
+
1234
+ // Add tags extracted from title
1235
+ if (titleTags && Array.isArray(titleTags)) {
1236
+ allTags.push(...titleTags);
1237
+ }
1238
+
1239
+ // Remove duplicates while preserving order
1240
+ const uniqueTags = [];
1241
+ const seen = new Set();
1242
+
1243
+ for (const tag of allTags) {
1244
+ if (!seen.has(tag) && tag.length > 0) {
1245
+ seen.add(tag);
1246
+ uniqueTags.push(tag);
1247
+ }
1248
+ }
1249
+
1250
+ return uniqueTags;
1251
+ }
1252
+
1075
1253
  async createTest(args) {
1076
1254
  const { suite_id, labels_ids, fields, ...attributes } = args;
1077
1255
 
1256
+ // Extract tags from title if provided
1257
+ const titleTags = this.extractTagsFromTitle(attributes.title);
1258
+ const mergedTags = this.mergeTags(attributes.tags, titleTags);
1259
+
1078
1260
  // Convert attributes to use hyphens instead of underscores for API compatibility
1079
1261
  const apiAttributes = Object.fromEntries(
1080
1262
  Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])
@@ -1085,12 +1267,16 @@ class TestomatioMCPServer {
1085
1267
  apiAttributes['suite_id'] = suite_id;
1086
1268
  }
1087
1269
 
1088
- // Handle fields parameter for custom fields
1270
+ // Build JSON-API request data
1089
1271
  const requestData = {
1090
- test: {
1091
- ...apiAttributes,
1092
- ...(labels_ids && { labels_ids: labels_ids }),
1093
- ...(fields && { custom_fields: fields })
1272
+ data: {
1273
+ type: 'tests',
1274
+ attributes: {
1275
+ ...apiAttributes,
1276
+ ...(mergedTags.length > 0 && { tags: mergedTags }),
1277
+ ...(labels_ids && { labels_ids: labels_ids }),
1278
+ ...(fields && { 'custom-fields': fields })
1279
+ }
1094
1280
  }
1095
1281
  };
1096
1282
 
@@ -1110,9 +1296,87 @@ class TestomatioMCPServer {
1110
1296
  };
1111
1297
  }
1112
1298
 
1299
+ async linkLabels(testId, labelsIds) {
1300
+ if (!labelsIds || labelsIds.length === 0) {
1301
+ return;
1302
+ }
1303
+
1304
+ // Process each label individually using the label linking API
1305
+ for (const labelId of labelsIds) {
1306
+ // Parse label:value format if present
1307
+ let labelUid = labelId;
1308
+ let value = null;
1309
+
1310
+ if (labelId.includes(':')) {
1311
+ [labelUid, value] = labelId.split(':', 2);
1312
+ }
1313
+
1314
+ // Build URL with test_id query parameter
1315
+ let url = `/labels/${labelUid}/link?test_id=${testId}`;
1316
+
1317
+ // Add value as query parameter if present
1318
+ if (value) {
1319
+ url += `&value=${encodeURIComponent(value)}`;
1320
+ }
1321
+
1322
+ await this.makePostRequest(url, {});
1323
+ }
1324
+ }
1325
+
1326
+ async unlinkLabel(args) {
1327
+ const { label_id, test_id, suite_id, value } = args;
1328
+
1329
+ // Validate that either test_id or suite_id is provided
1330
+ if (!test_id && !suite_id) {
1331
+ throw new Error('Either test_id or suite_id must be provided');
1332
+ }
1333
+
1334
+ if (test_id && suite_id) {
1335
+ throw new Error('Cannot specify both test_id and suite_id. Use one or the other.');
1336
+ }
1337
+
1338
+ // Build URL with base label link endpoint
1339
+ let url = `/labels/${label_id}/link`;
1340
+
1341
+ // Add appropriate query parameters
1342
+ if (test_id) {
1343
+ url += `?test_id=${test_id}`;
1344
+ } else if (suite_id) {
1345
+ url += `?suite_id=${suite_id}`;
1346
+ }
1347
+
1348
+ // Add event=remove parameter
1349
+ url += `&event=remove`;
1350
+
1351
+ // Add value as query parameter if present
1352
+ if (value) {
1353
+ url += `&value=${encodeURIComponent(value)}`;
1354
+ }
1355
+
1356
+ // Make the API request
1357
+ await this.makePostRequest(url, {});
1358
+
1359
+ const itemType = test_id ? 'test' : 'suite';
1360
+ const itemId = test_id || suite_id;
1361
+ const removeDescription = value ? `value "${value}"` : 'all instances';
1362
+
1363
+ return {
1364
+ content: [
1365
+ {
1366
+ type: 'text',
1367
+ text: `Successfully removed label "${label_id}" (${removeDescription}) from ${itemType} "${itemId}"`,
1368
+ },
1369
+ ],
1370
+ };
1371
+ }
1372
+
1113
1373
  async updateTest(args) {
1114
1374
  const { test_id, suite_id, labels_ids, fields, ...attributes } = args;
1115
1375
 
1376
+ // Extract tags from title if provided
1377
+ const titleTags = this.extractTagsFromTitle(attributes.title);
1378
+ const mergedTags = this.mergeTags(attributes.tags, titleTags);
1379
+
1116
1380
  // Convert attributes to use hyphens instead of underscores for API compatibility
1117
1381
  const apiAttributes = Object.fromEntries(
1118
1382
  Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])
@@ -1125,29 +1389,27 @@ class TestomatioMCPServer {
1125
1389
 
1126
1390
  let data;
1127
1391
 
1128
- // Handle labels_ids using the bulk labels endpoint
1129
- if (labels_ids && labels_ids.length > 0) {
1130
- const labelsRequest = {
1131
- items: {
1132
- test: [test_id]
1133
- },
1134
- add_labels: labels_ids
1135
- };
1136
-
1137
- await this.makePutRequest('/bulk/labels', labelsRequest);
1138
-
1139
- // After updating labels, fetch the updated test
1140
- data = await this.makeRequest(`/tests/${test_id}`);
1141
- } else {
1142
- // Handle regular test attributes update
1143
- const requestData = {
1144
- test: {
1392
+ // Handle regular test attributes update using JSON-API format
1393
+ const requestData = {
1394
+ data: {
1395
+ id: test_id,
1396
+ type: 'tests',
1397
+ attributes: {
1145
1398
  ...apiAttributes,
1146
- ...(fields && { custom_fields: fields })
1399
+ ...(mergedTags.length > 0 && { tags: mergedTags }),
1400
+ ...(fields && { 'custom-fields': fields })
1147
1401
  }
1148
- };
1402
+ }
1403
+ };
1149
1404
 
1150
- data = await this.makePutRequest(`/tests/${test_id}`, requestData);
1405
+ data = await this.makePutRequest(`/tests/${test_id}`, requestData);
1406
+
1407
+ // Handle labels_ids using the label linking API
1408
+ if (labels_ids && labels_ids.length > 0) {
1409
+ await this.linkLabels(test_id, labels_ids);
1410
+
1411
+ // After linking labels, fetch the updated test to reflect changes
1412
+ data = await this.makeRequest(`/tests/${test_id}`);
1151
1413
  }
1152
1414
 
1153
1415
  const formattedTest = this.formatModel(data.data, 'test', [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/mcp",
3
- "version": "1.0.8",
3
+ "version": "1.0.11",
4
4
  "description": "Model Context Protocol server for Testomatio API",
5
5
  "main": "index.js",
6
6
  "bin": {