@testomatio/mcp 1.0.6 → 1.0.8

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 +6 -6
  2. package/index.js +61 -43
  3. package/package.json +5 -2
package/README.md CHANGED
@@ -13,7 +13,7 @@ A Model Context Protocol (MCP) server for Testomat.io API integration with AI as
13
13
  ### Run directly with npx
14
14
 
15
15
  ```bash
16
- npx @testomatio/mcp --token <your-token> --project <project-id>
16
+ npx @testomatio/mcp@latest --token <your-token> --project <project-id>
17
17
  ```
18
18
 
19
19
  ## Usage
@@ -26,13 +26,13 @@ The MCP server can be started using command line arguments or environment variab
26
26
 
27
27
  ```bash
28
28
  # Using short flags
29
- npx @testomatio/mcp -t testomat_YOUR_TOKEN_HERE -p your-project-id
29
+ npx @testomatio/mcp@latest -t testomat_YOUR_TOKEN_HERE -p your-project-id
30
30
 
31
31
  # Using long flags
32
- npx @testomatio/mcp --token testomat_YOUR_TOKEN_HERE --project your-project-id
32
+ npx @testomatio/mcp@latest --token testomat_YOUR_TOKEN_HERE --project your-project-id
33
33
 
34
34
  # With custom base URL
35
- npx @testomatio/mcp --token testomat_YOUR_TOKEN_HERE --project your-project-id --base-url https://your-instance.testomat.io
35
+ npx @testomatio/mcp@latest --token testomat_YOUR_TOKEN_HERE --project your-project-id --base-url https://your-instance.testomat.io
36
36
  ```
37
37
 
38
38
  #### Using Environment Variables
@@ -43,10 +43,10 @@ export TESTOMATIO_API_TOKEN=testomat_YOUR_TOKEN_HERE
43
43
  export TESTOMATIO_BASE_URL=https://app.testomat.io # Optional, defaults to https://app.testomat.io
44
44
 
45
45
  # Run with project ID
46
- npx @testomatio/mcp --project your-project-id
46
+ npx @testomatio/mcp@latest --project your-project-id
47
47
 
48
48
  # Or run directly with environment variables
49
- TESTOMATIO_API_TOKEN=testomat_YOUR_TOKEN_HERE npx @testomatio/mcp --project your-project-id
49
+ TESTOMATIO_API_TOKEN=testomat_YOUR_TOKEN_HERE npx @testomatio/mcp@latest --project your-project-id
50
50
  ```
51
51
 
52
52
  ### Getting Your API Token
package/index.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  ListToolsRequestSchema,
8
8
  } from '@modelcontextprotocol/sdk/types.js';
9
9
  import { program } from 'commander';
10
+ import { fileURLToPath } from 'url';
10
11
 
11
12
  function normalizeString(value) {
12
13
  return typeof value === 'string' ? value.trim() : value;
@@ -642,7 +643,8 @@ class TestomatioMCPServer {
642
643
  this.jwtToken = null;
643
644
  return this.makePostRequest(path, data);
644
645
  }
645
- throw new Error(`HTTP ${response.status}: ${response.statusText}; ${await response.text()}`);
646
+ const errorText = await response.text();
647
+ throw new Error(`HTTP ${response.status}: ${response.statusText}. Details: ${errorText}`);
646
648
  }
647
649
 
648
650
  return await response.json();
@@ -666,7 +668,8 @@ class TestomatioMCPServer {
666
668
  this.jwtToken = null;
667
669
  return this.makePutRequest(path, data);
668
670
  }
669
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
671
+ const errorText = await response.text();
672
+ throw new Error(`HTTP ${response.status}: ${response.statusText}. Details: ${errorText}`);
670
673
  }
671
674
 
672
675
  return await response.json();
@@ -1072,29 +1075,24 @@ class TestomatioMCPServer {
1072
1075
  async createTest(args) {
1073
1076
  const { suite_id, labels_ids, fields, ...attributes } = args;
1074
1077
 
1075
- // Handle fields parameter for custom fields
1076
- const requestData = {
1077
- data: {
1078
- type: 'tests',
1079
- attributes: {
1080
- ...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
1081
- ...(fields && { 'custom-fields': fields })
1082
- }
1083
- }
1084
- };
1078
+ // Convert attributes to use hyphens instead of underscores for API compatibility
1079
+ const apiAttributes = Object.fromEntries(
1080
+ Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])
1081
+ );
1085
1082
 
1083
+ // Add suite_id to attributes if provided
1086
1084
  if (suite_id) {
1087
- requestData.data.relationships = {
1088
- suite: {
1089
- data: {
1090
- type: 'suites',
1091
- id: suite_id
1092
- }
1093
- }
1094
- };
1085
+ apiAttributes['suite_id'] = suite_id;
1095
1086
  }
1096
1087
 
1097
- if (labels_ids) requestData.labels_ids = labels_ids;
1088
+ // Handle fields parameter for custom fields
1089
+ const requestData = {
1090
+ test: {
1091
+ ...apiAttributes,
1092
+ ...(labels_ids && { labels_ids: labels_ids }),
1093
+ ...(fields && { custom_fields: fields })
1094
+ }
1095
+ };
1098
1096
 
1099
1097
  const data = await this.makePostRequest('/tests', requestData);
1100
1098
  const formattedTest = this.formatModel(data.data, 'test', [
@@ -1115,31 +1113,43 @@ class TestomatioMCPServer {
1115
1113
  async updateTest(args) {
1116
1114
  const { test_id, suite_id, labels_ids, fields, ...attributes } = args;
1117
1115
 
1118
- // Handle fields parameter for custom fields
1119
- const requestData = {
1120
- data: {
1121
- type: 'tests',
1122
- attributes: {
1123
- ...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
1124
- ...(fields && { 'custom-fields': fields })
1125
- }
1126
- }
1127
- };
1116
+ // Convert attributes to use hyphens instead of underscores for API compatibility
1117
+ const apiAttributes = Object.fromEntries(
1118
+ Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])
1119
+ );
1128
1120
 
1121
+ // Add suite_id to attributes if provided
1129
1122
  if (suite_id) {
1130
- requestData.data.relationships = {
1131
- suite: {
1132
- data: {
1133
- type: 'suites',
1134
- id: suite_id
1135
- }
1123
+ apiAttributes['suite_id'] = suite_id;
1124
+ }
1125
+
1126
+ let data;
1127
+
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: {
1145
+ ...apiAttributes,
1146
+ ...(fields && { custom_fields: fields })
1136
1147
  }
1137
1148
  };
1138
- }
1139
1149
 
1140
- if (labels_ids) requestData.labels_ids = labels_ids;
1150
+ data = await this.makePutRequest(`/tests/${test_id}`, requestData);
1151
+ }
1141
1152
 
1142
- const data = await this.makePutRequest(`/tests/${test_id}`, requestData);
1143
1153
  const formattedTest = this.formatModel(data.data, 'test', [
1144
1154
  'title', 'description', 'code', 'priority',
1145
1155
  'state', 'suite-id', 'tags', 'file'
@@ -1338,7 +1348,15 @@ async function main() {
1338
1348
  }
1339
1349
  }
1340
1350
 
1341
- // Only run main() if this file is executed directly (not imported)
1342
- if (import.meta.url === `file://${process.argv[1]}`) {
1343
- main().catch(console.error);
1351
+ // Run main() when executed directly or as a bin script
1352
+ // Don't run when imported as a module for testing
1353
+ if (import.meta.url.startsWith('file://') && process.argv[1]) {
1354
+ const modulePath = fileURLToPath(import.meta.url);
1355
+ const isDirectExecution = process.argv[1].includes('index.js') ||
1356
+ process.argv[1] === modulePath ||
1357
+ process.argv[1].endsWith('/testomatio-mcp');
1358
+
1359
+ if (isDirectExecution) {
1360
+ main().catch(console.error);
1361
+ }
1344
1362
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/mcp",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Model Context Protocol server for Testomatio API",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -13,11 +13,14 @@
13
13
  "test": "node --experimental-vm-modules node_modules/.bin/jest",
14
14
  "test:unit": "node --experimental-vm-modules node_modules/.bin/jest",
15
15
  "test:integration": "node --experimental-vm-modules node_modules/.bin/jest --config jest.integration.config.js",
16
+ "test:e2e": "node --experimental-vm-modules node_modules/.bin/jest --config jest.e2e.config.js",
16
17
  "test:watch": "node --experimental-vm-modules node_modules/.bin/jest --watch",
17
18
  "test:integration:watch": "node --experimental-vm-modules node_modules/.bin/jest --config jest.integration.config.js --watch",
19
+ "test:e2e:watch": "node --experimental-vm-modules node_modules/.bin/jest --config jest.e2e.config.js --watch",
18
20
  "test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
19
21
  "test:coverage:integration": "node --experimental-vm-modules node_modules/.bin/jest --config jest.integration.config.js --coverage",
20
- "test:all": "node --experimental-vm-modules node_modules/.bin/jest --config jest.integration.config.js"
22
+ "test:coverage:e2e": "node --experimental-vm-modules node_modules/.bin/jest --config jest.e2e.config.js --coverage",
23
+ "test:all": "npm run test:unit && npm run test:integration && npm run test:e2e"
21
24
  },
22
25
  "keywords": [
23
26
  "testomatio",