@testomatio/mcp 1.0.3 → 1.0.5
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 +169 -5
- package/index.js +284 -14
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -112,13 +112,79 @@ 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`, `fields`) — api: POST `/suites`
|
|
123
|
+
* `create_folder` – Create a new folder (params: `title`, `description`, `parent_id`, `fields`) — api: POST `/suites`
|
|
124
|
+
|
|
125
|
+
#### Labels
|
|
126
|
+
* `create_label` – Create a new label with optional custom field (params: `title`, `color`, `scope`, `visibility`, `field`) — api: POST `/labels`
|
|
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
|
+
```
|
|
122
188
|
|
|
123
189
|
#### Test Runs
|
|
124
190
|
* `get_runs` – List all runs (no params) — api: GET `/runs`
|
|
@@ -141,6 +207,12 @@ Once configured, you can ask your AI assistant questions like:
|
|
|
141
207
|
- "Get all test plans for this project"
|
|
142
208
|
- "Create a new test called 'Login validation' in suite suite-123"
|
|
143
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"
|
|
212
|
+
- "Create a new suite called 'Authentication Tests' with description 'All login and signup related tests'"
|
|
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"
|
|
215
|
+
- "Create a label called 'Severity' with color '#ffe9ad' and predefined values like 'Blocker', 'Critical', 'Major', 'Minor', 'Normal', 'Trivial'"
|
|
144
216
|
|
|
145
217
|
## Query Patterns
|
|
146
218
|
|
|
@@ -160,6 +232,31 @@ These queries allow creating and updating tests:
|
|
|
160
232
|
- **"Create a new test called 'Login validation' in suite suite-123"** → `create_test` tool with `title: "Login validation"`, `suite_id: "suite-123"`
|
|
161
233
|
- **"Update test test-456 to change its description"** → `update_test` tool with `test_id: "test-456"`, `description: "new description"`
|
|
162
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" }`
|
|
237
|
+
|
|
238
|
+
### Suite and Folder Management Queries
|
|
239
|
+
|
|
240
|
+
These queries help organize your test structure:
|
|
241
|
+
|
|
242
|
+
- **"Create a new suite called 'Authentication Tests'"** → `create_suite` tool with `title: "Authentication Tests"`
|
|
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" }`
|
|
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" }`
|
|
247
|
+
- **"Create a test suite for payment features"** → `create_suite` tool with `title: "Payment Features", description: "Tests covering payment processing"`
|
|
248
|
+
- **"Create a folder to organize integration tests"** → `create_folder` tool with `title: "Integration Tests"`
|
|
249
|
+
|
|
250
|
+
**Note**: Suites can only contain other suites, while folders can contain both suites and folders (but no tests).
|
|
251
|
+
|
|
252
|
+
### Label Creation Queries
|
|
253
|
+
|
|
254
|
+
These queries help create custom labels for better test categorization:
|
|
255
|
+
|
|
256
|
+
- **"Create a label called 'Severity' with red color"** → `create_label` tool with `title: "Severity"`, `color: "#ff6b6b"`
|
|
257
|
+
- **"Create a severity label with predefined values"** → `create_label` tool with `title: "Severity"`, `color: "#ffe9ad"`, `field: { "type": "list", "short": true, "value": "Blocker\nCritical\nMajor\nNormal\nMinor\nTrivial" }`
|
|
258
|
+
- **"Create a simple label for test types"** → `create_label` tool with `title: "Test Type"`, `scope: ["tests", "suites"]`
|
|
259
|
+
- **"Create a label visible in test lists"** → `create_label` tool with `title: "Category"`, `visibility: ["list"]`
|
|
163
260
|
|
|
164
261
|
### Specific Item Queries
|
|
165
262
|
|
|
@@ -174,9 +271,8 @@ These queries target specific entities by ID:
|
|
|
174
271
|
These queries use advanced filtering capabilities:
|
|
175
272
|
|
|
176
273
|
- **"List all automated tests with the @smoke tag"** → `search_tests` tool with `query: "@smoke"`, `state: "automated"`
|
|
177
|
-
- **"Find tests with priority high"** → `search_tests` tool with `priority: "high"`
|
|
178
274
|
- **"Search for tests containing 'login'"** → `search_tests` tool with `query: "login"`
|
|
179
|
-
- **"List tests tagged @critical or labelled 'ux' with
|
|
275
|
+
- **"List tests tagged @critical or labelled 'ux' with critical severity"** → `search_tests` tool with `tql: "tag == 'critical' or label == 'ux' and severity == 'critical'"`
|
|
180
276
|
- **"Find tests linked to JIRA-123"** → `search_tests` tool with `tql: jira == 'BDCP-2'`
|
|
181
277
|
|
|
182
278
|
### Advanced Query Syntax
|
|
@@ -187,7 +283,7 @@ The `search_tests` tool supports TQL for complex filtering:
|
|
|
187
283
|
|
|
188
284
|
```
|
|
189
285
|
"tag == 'smoke' and state == 'manual'"
|
|
190
|
-
"
|
|
286
|
+
"severity == 'critical' or label == 'ux'"
|
|
191
287
|
```
|
|
192
288
|
|
|
193
289
|
#### Tag-Based Searches
|
|
@@ -247,6 +343,74 @@ For detailed information about the underlying Testomat.io API, refer to the [Tes
|
|
|
247
343
|
|
|
248
344
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
249
345
|
|
|
346
|
+
### Development Setup
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# Clone the repository
|
|
350
|
+
git clone https://github.com/testomatio/mcp.git
|
|
351
|
+
cd mcp
|
|
352
|
+
|
|
353
|
+
# Install dependencies
|
|
354
|
+
npm install
|
|
355
|
+
|
|
356
|
+
# Run unit tests
|
|
357
|
+
npm test
|
|
358
|
+
|
|
359
|
+
# Run integration tests (requires environment variables)
|
|
360
|
+
npm run test:integration
|
|
361
|
+
|
|
362
|
+
# Run all tests
|
|
363
|
+
npm run test:all
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Testing
|
|
367
|
+
|
|
368
|
+
The project includes comprehensive test coverage:
|
|
369
|
+
|
|
370
|
+
- **Unit Tests**: Fast tests with mocked dependencies
|
|
371
|
+
- **Integration Tests**: Real API tests against Testomat.io
|
|
372
|
+
|
|
373
|
+
#### Running Tests Locally
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
# Unit tests only
|
|
377
|
+
npm run test:unit
|
|
378
|
+
|
|
379
|
+
# Integration tests (requires .env file)
|
|
380
|
+
npm run test:integration
|
|
381
|
+
|
|
382
|
+
# With coverage
|
|
383
|
+
npm run test:coverage
|
|
384
|
+
npm run test:coverage:integration
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
#### Environment Setup for Integration Tests
|
|
388
|
+
|
|
389
|
+
Create a `.env` file:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
TESTOMATIO_API_TOKEN=testomat_your_token_here
|
|
393
|
+
TESTOMATIO_PROJECT_ID=your_project_id
|
|
394
|
+
TESTOMATIO_BASE_URL=https://app.testomat.io # optional
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### CI/CD
|
|
398
|
+
|
|
399
|
+
This project uses GitHub Actions for continuous integration:
|
|
400
|
+
|
|
401
|
+
- ✅ **Unit Tests**: Run on every push/PR across Node.js 18, 20, 22
|
|
402
|
+
- ✅ **Integration Tests**: Run daily and on main branch merges
|
|
403
|
+
- ✅ **Coverage Reports**: Automatic upload to Codecov
|
|
404
|
+
- ✅ **Security**: Secrets management for API credentials
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
### Code Quality
|
|
408
|
+
|
|
409
|
+
- Follow existing code style patterns
|
|
410
|
+
- Add tests for new functionality
|
|
411
|
+
- Update documentation when needed
|
|
412
|
+
- Ensure all tests pass before submitting PRs
|
|
413
|
+
|
|
250
414
|
## License
|
|
251
415
|
|
|
252
416
|
This project is licensed under the MIT License - see the LICENSE file for details.
|
package/index.js
CHANGED
|
@@ -41,7 +41,8 @@ class TestomatioMCPServer {
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
if (!response.ok) {
|
|
44
|
-
|
|
44
|
+
const errorText = await response.text();
|
|
45
|
+
throw new Error(`Authentication failed: HTTP ${response.status}: ${response.statusText}. Response: ${errorText}`);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
const data = await response.json();
|
|
@@ -317,7 +318,14 @@ class TestomatioMCPServer {
|
|
|
317
318
|
labels_ids: {
|
|
318
319
|
type: 'array',
|
|
319
320
|
items: { type: 'string' },
|
|
320
|
-
description: 'Slugs of labels to assign to the test',
|
|
321
|
+
description: 'Slugs of labels to assign to the test. Supports label:value format (e.g., ["priority:high", "severity:critical"])',
|
|
322
|
+
},
|
|
323
|
+
fields: {
|
|
324
|
+
type: 'object',
|
|
325
|
+
description: 'Set custom fields for the test. Object with field names as keys and values as properties (e.g., {"priority": "high", "severity": "critical"})',
|
|
326
|
+
additionalProperties: {
|
|
327
|
+
type: 'string'
|
|
328
|
+
},
|
|
321
329
|
},
|
|
322
330
|
},
|
|
323
331
|
required: ['suite_id', 'title'],
|
|
@@ -358,6 +366,11 @@ class TestomatioMCPServer {
|
|
|
358
366
|
enum: ['manual', 'automated'],
|
|
359
367
|
description: 'State of the test',
|
|
360
368
|
},
|
|
369
|
+
priority: {
|
|
370
|
+
type: 'string',
|
|
371
|
+
enum: ['low', 'normal', 'high', 'critical'],
|
|
372
|
+
description: 'Priority level of the test',
|
|
373
|
+
},
|
|
361
374
|
tags: {
|
|
362
375
|
type: 'array',
|
|
363
376
|
items: { type: 'string' },
|
|
@@ -375,12 +388,129 @@ class TestomatioMCPServer {
|
|
|
375
388
|
labels_ids: {
|
|
376
389
|
type: 'array',
|
|
377
390
|
items: { type: 'string' },
|
|
378
|
-
description: 'Slugs of labels to assign to the test',
|
|
391
|
+
description: 'Slugs of labels to assign to the test. Supports label:value format (e.g., ["priority:high", "severity:critical"])',
|
|
392
|
+
},
|
|
393
|
+
fields: {
|
|
394
|
+
type: 'object',
|
|
395
|
+
description: 'Set custom fields for the test. Object with field names as keys and values as properties (e.g., {"priority": "high", "severity": "critical"})',
|
|
396
|
+
additionalProperties: {
|
|
397
|
+
type: 'string'
|
|
398
|
+
},
|
|
379
399
|
},
|
|
380
400
|
},
|
|
381
401
|
required: ['test_id'],
|
|
382
402
|
},
|
|
383
403
|
},
|
|
404
|
+
{
|
|
405
|
+
name: 'create_suite',
|
|
406
|
+
description: 'Create a new suite. Suites can only contain other suites (no tests or folders)',
|
|
407
|
+
inputSchema: {
|
|
408
|
+
type: 'object',
|
|
409
|
+
properties: {
|
|
410
|
+
title: {
|
|
411
|
+
type: 'string',
|
|
412
|
+
description: 'Suite title',
|
|
413
|
+
},
|
|
414
|
+
description: {
|
|
415
|
+
type: 'string',
|
|
416
|
+
description: 'Suite description',
|
|
417
|
+
},
|
|
418
|
+
parent_id: {
|
|
419
|
+
type: 'string',
|
|
420
|
+
description: 'Parent suite ID to create this suite under',
|
|
421
|
+
},
|
|
422
|
+
fields: {
|
|
423
|
+
type: 'object',
|
|
424
|
+
description: 'Set custom fields for the suite. Object with field names as keys and values as properties (e.g., {"priority": "high", "team": "backend"})',
|
|
425
|
+
additionalProperties: {
|
|
426
|
+
type: 'string'
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
required: ['title'],
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
name: 'create_folder',
|
|
435
|
+
description: 'Create a new folder. Folders can contain suites and folders (but no tests)',
|
|
436
|
+
inputSchema: {
|
|
437
|
+
type: 'object',
|
|
438
|
+
properties: {
|
|
439
|
+
title: {
|
|
440
|
+
type: 'string',
|
|
441
|
+
description: 'Folder title',
|
|
442
|
+
},
|
|
443
|
+
description: {
|
|
444
|
+
type: 'string',
|
|
445
|
+
description: 'Folder description',
|
|
446
|
+
},
|
|
447
|
+
parent_id: {
|
|
448
|
+
type: 'string',
|
|
449
|
+
description: 'Parent folder or suite ID to create this folder under',
|
|
450
|
+
},
|
|
451
|
+
fields: {
|
|
452
|
+
type: 'object',
|
|
453
|
+
description: 'Set custom fields for the folder. Object with field names as keys and values as properties (e.g., {"priority": "high", "team": "backend"})',
|
|
454
|
+
additionalProperties: {
|
|
455
|
+
type: 'string'
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
required: ['title'],
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
name: 'create_label',
|
|
464
|
+
description: 'Create a new label with optional custom field configuration. Labels can be used to tag and categorize tests and suites',
|
|
465
|
+
inputSchema: {
|
|
466
|
+
type: 'object',
|
|
467
|
+
properties: {
|
|
468
|
+
title: {
|
|
469
|
+
type: 'string',
|
|
470
|
+
description: 'Label title (e.g., "Severity", "Priority", "Type")',
|
|
471
|
+
},
|
|
472
|
+
color: {
|
|
473
|
+
type: 'string',
|
|
474
|
+
description: 'Label color in hex format (e.g., "#ffe9ad")',
|
|
475
|
+
},
|
|
476
|
+
scope: {
|
|
477
|
+
type: 'array',
|
|
478
|
+
items: {
|
|
479
|
+
type: 'string',
|
|
480
|
+
enum: ['tests', 'suites']
|
|
481
|
+
},
|
|
482
|
+
description: 'Where this label can be used (e.g., ["tests", "suites"])',
|
|
483
|
+
},
|
|
484
|
+
visibility: {
|
|
485
|
+
type: 'array',
|
|
486
|
+
items: {
|
|
487
|
+
type: 'string'
|
|
488
|
+
},
|
|
489
|
+
description: 'Where the label is visible (e.g., ["list"])',
|
|
490
|
+
},
|
|
491
|
+
field: {
|
|
492
|
+
type: 'object',
|
|
493
|
+
description: 'Custom field configuration for labels with predefined values',
|
|
494
|
+
properties: {
|
|
495
|
+
type: {
|
|
496
|
+
type: 'string',
|
|
497
|
+
description: 'Field type (e.g., "list", "string", "number")',
|
|
498
|
+
},
|
|
499
|
+
short: {
|
|
500
|
+
type: 'boolean',
|
|
501
|
+
description: 'Whether to display short version',
|
|
502
|
+
},
|
|
503
|
+
value: {
|
|
504
|
+
type: 'string',
|
|
505
|
+
description: 'Predefined values for the field (newline separated for list type)',
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
required: ['type'],
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
required: ['title'],
|
|
512
|
+
},
|
|
513
|
+
},
|
|
384
514
|
],
|
|
385
515
|
};
|
|
386
516
|
});
|
|
@@ -414,6 +544,12 @@ class TestomatioMCPServer {
|
|
|
414
544
|
return await this.createTest(args);
|
|
415
545
|
case 'update_test':
|
|
416
546
|
return await this.updateTest(args);
|
|
547
|
+
case 'create_suite':
|
|
548
|
+
return await this.createSuite(args);
|
|
549
|
+
case 'create_folder':
|
|
550
|
+
return await this.createFolder(args);
|
|
551
|
+
case 'create_label':
|
|
552
|
+
return await this.createLabel(args);
|
|
417
553
|
default:
|
|
418
554
|
throw new Error(`Unknown tool: ${name}`);
|
|
419
555
|
}
|
|
@@ -914,14 +1050,19 @@ class TestomatioMCPServer {
|
|
|
914
1050
|
}
|
|
915
1051
|
|
|
916
1052
|
async createTest(args) {
|
|
917
|
-
const { suite_id, labels_ids, ...attributes } = args;
|
|
1053
|
+
const { suite_id, labels_ids, fields, ...attributes } = args;
|
|
1054
|
+
|
|
1055
|
+
// Handle fields parameter for custom fields
|
|
918
1056
|
const requestData = {
|
|
919
1057
|
data: {
|
|
920
1058
|
type: 'tests',
|
|
921
|
-
attributes:
|
|
1059
|
+
attributes: {
|
|
1060
|
+
...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
|
|
1061
|
+
...(fields && { 'custom-fields': fields })
|
|
1062
|
+
}
|
|
922
1063
|
}
|
|
923
1064
|
};
|
|
924
|
-
|
|
1065
|
+
|
|
925
1066
|
if (suite_id) {
|
|
926
1067
|
requestData.data.relationships = {
|
|
927
1068
|
suite: {
|
|
@@ -932,7 +1073,7 @@ class TestomatioMCPServer {
|
|
|
932
1073
|
}
|
|
933
1074
|
};
|
|
934
1075
|
}
|
|
935
|
-
|
|
1076
|
+
|
|
936
1077
|
if (labels_ids) requestData.labels_ids = labels_ids;
|
|
937
1078
|
|
|
938
1079
|
const data = await this.makePostRequest('/tests', requestData);
|
|
@@ -952,14 +1093,19 @@ class TestomatioMCPServer {
|
|
|
952
1093
|
}
|
|
953
1094
|
|
|
954
1095
|
async updateTest(args) {
|
|
955
|
-
const { test_id, suite_id, labels_ids, ...attributes } = args;
|
|
1096
|
+
const { test_id, suite_id, labels_ids, fields, ...attributes } = args;
|
|
1097
|
+
|
|
1098
|
+
// Handle fields parameter for custom fields
|
|
956
1099
|
const requestData = {
|
|
957
1100
|
data: {
|
|
958
1101
|
type: 'tests',
|
|
959
|
-
attributes:
|
|
1102
|
+
attributes: {
|
|
1103
|
+
...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
|
|
1104
|
+
...(fields && { 'custom-fields': fields })
|
|
1105
|
+
}
|
|
960
1106
|
}
|
|
961
1107
|
};
|
|
962
|
-
|
|
1108
|
+
|
|
963
1109
|
if (suite_id) {
|
|
964
1110
|
requestData.data.relationships = {
|
|
965
1111
|
suite: {
|
|
@@ -970,7 +1116,7 @@ class TestomatioMCPServer {
|
|
|
970
1116
|
}
|
|
971
1117
|
};
|
|
972
1118
|
}
|
|
973
|
-
|
|
1119
|
+
|
|
974
1120
|
if (labels_ids) requestData.labels_ids = labels_ids;
|
|
975
1121
|
|
|
976
1122
|
const data = await this.makePutRequest(`/tests/${test_id}`, requestData);
|
|
@@ -989,6 +1135,124 @@ class TestomatioMCPServer {
|
|
|
989
1135
|
};
|
|
990
1136
|
}
|
|
991
1137
|
|
|
1138
|
+
async createSuite(args) {
|
|
1139
|
+
const { parent_id, fields, ...attributes } = args;
|
|
1140
|
+
const requestData = {
|
|
1141
|
+
data: {
|
|
1142
|
+
type: 'suites',
|
|
1143
|
+
attributes: {
|
|
1144
|
+
...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
|
|
1145
|
+
'file-type': 'file',
|
|
1146
|
+
...(fields && { 'custom-fields': fields })
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
if (parent_id) {
|
|
1152
|
+
requestData.data.relationships = {
|
|
1153
|
+
parent: {
|
|
1154
|
+
data: {
|
|
1155
|
+
type: 'suites',
|
|
1156
|
+
id: parent_id
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
const data = await this.makePostRequest('/suites', requestData);
|
|
1163
|
+
const formattedSuite = this.formatModel(data.data, 'suite', [
|
|
1164
|
+
'title', 'description', 'test-count', 'is-root', 'file-type'
|
|
1165
|
+
]);
|
|
1166
|
+
|
|
1167
|
+
return {
|
|
1168
|
+
content: [
|
|
1169
|
+
{
|
|
1170
|
+
type: 'text',
|
|
1171
|
+
text: `Successfully created suite:\n\n${formattedSuite}`,
|
|
1172
|
+
},
|
|
1173
|
+
],
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
async createFolder(args) {
|
|
1178
|
+
const { parent_id, fields, ...attributes } = args;
|
|
1179
|
+
const requestData = {
|
|
1180
|
+
data: {
|
|
1181
|
+
type: 'suites',
|
|
1182
|
+
attributes: {
|
|
1183
|
+
...Object.fromEntries(Object.entries(attributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
|
|
1184
|
+
'file-type': 'folder',
|
|
1185
|
+
...(fields && { 'custom-fields': fields })
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
if (parent_id) {
|
|
1191
|
+
requestData.data.relationships = {
|
|
1192
|
+
parent: {
|
|
1193
|
+
data: {
|
|
1194
|
+
type: 'suites',
|
|
1195
|
+
id: parent_id
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
const data = await this.makePostRequest('/suites', requestData);
|
|
1202
|
+
const formattedSuite = this.formatModel(data.data, 'suite', [
|
|
1203
|
+
'title', 'description', 'test-count', 'is-root', 'file-type'
|
|
1204
|
+
]);
|
|
1205
|
+
|
|
1206
|
+
return {
|
|
1207
|
+
content: [
|
|
1208
|
+
{
|
|
1209
|
+
type: 'text',
|
|
1210
|
+
text: `Successfully created folder:\n\n${formattedSuite}`,
|
|
1211
|
+
},
|
|
1212
|
+
],
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
async createLabel(args) {
|
|
1217
|
+
const { title, color, scope, visibility, field, ...otherAttributes } = args;
|
|
1218
|
+
|
|
1219
|
+
const requestData = {
|
|
1220
|
+
data: {
|
|
1221
|
+
type: 'labels',
|
|
1222
|
+
attributes: {
|
|
1223
|
+
...Object.fromEntries(Object.entries(otherAttributes).map(([k, v]) => [k.replace(/_/g, '-'), v])),
|
|
1224
|
+
title,
|
|
1225
|
+
color,
|
|
1226
|
+
scope,
|
|
1227
|
+
visibility
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
// Add field configuration if provided
|
|
1233
|
+
if (field) {
|
|
1234
|
+
requestData.data.attributes.field = {
|
|
1235
|
+
type: field.type,
|
|
1236
|
+
...(field.short !== undefined && { short: field.short }),
|
|
1237
|
+
...(field.value && { value: field.value })
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
const data = await this.makePostRequest('/labels', requestData);
|
|
1242
|
+
const formattedLabel = this.formatModel(data.data, 'label', [
|
|
1243
|
+
'title', 'color', 'scope', 'visibility', 'field'
|
|
1244
|
+
]);
|
|
1245
|
+
|
|
1246
|
+
return {
|
|
1247
|
+
content: [
|
|
1248
|
+
{
|
|
1249
|
+
type: 'text',
|
|
1250
|
+
text: `Successfully created label:\n\n${formattedLabel}`,
|
|
1251
|
+
},
|
|
1252
|
+
],
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
|
|
992
1256
|
async run() {
|
|
993
1257
|
// Test authentication on startup
|
|
994
1258
|
try {
|
|
@@ -1005,6 +1269,9 @@ class TestomatioMCPServer {
|
|
|
1005
1269
|
}
|
|
1006
1270
|
}
|
|
1007
1271
|
|
|
1272
|
+
// Export the class for testing
|
|
1273
|
+
export { TestomatioMCPServer };
|
|
1274
|
+
|
|
1008
1275
|
// Parse command line arguments using commander
|
|
1009
1276
|
function parseArgs() {
|
|
1010
1277
|
program
|
|
@@ -1019,7 +1286,7 @@ function parseArgs() {
|
|
|
1019
1286
|
const options = program.opts();
|
|
1020
1287
|
|
|
1021
1288
|
const token = options.token || process.env.TESTOMATIO_API_TOKEN;
|
|
1022
|
-
const projectId = options.project;
|
|
1289
|
+
const projectId = options.project || process.env.TESTOMATIO_PROJECT_ID;
|
|
1023
1290
|
const baseUrl = options.baseUrl || process.env.TESTOMATIO_BASE_URL || 'https://app.testomat.io';
|
|
1024
1291
|
|
|
1025
1292
|
if (!token) {
|
|
@@ -1028,7 +1295,7 @@ function parseArgs() {
|
|
|
1028
1295
|
}
|
|
1029
1296
|
|
|
1030
1297
|
if (!projectId) {
|
|
1031
|
-
console.error('Error: Project ID is required. Use --project <project_id>');
|
|
1298
|
+
console.error('Error: Project ID is required. Use --project <project_id> or set TESTOMATIO_PROJECT_ID environment variable');
|
|
1032
1299
|
process.exit(1);
|
|
1033
1300
|
}
|
|
1034
1301
|
|
|
@@ -1047,4 +1314,7 @@ async function main() {
|
|
|
1047
1314
|
}
|
|
1048
1315
|
}
|
|
1049
1316
|
|
|
1050
|
-
main()
|
|
1317
|
+
// Only run main() if this file is executed directly (not imported)
|
|
1318
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
1319
|
+
main().catch(console.error);
|
|
1320
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Model Context Protocol server for Testomatio API",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,14 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node index.js",
|
|
12
12
|
"dev": "node index.js",
|
|
13
|
-
"test": "
|
|
13
|
+
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
|
14
|
+
"test:unit": "node --experimental-vm-modules node_modules/.bin/jest",
|
|
15
|
+
"test:integration": "node --experimental-vm-modules node_modules/.bin/jest --config jest.integration.config.js",
|
|
16
|
+
"test:watch": "node --experimental-vm-modules node_modules/.bin/jest --watch",
|
|
17
|
+
"test:integration:watch": "node --experimental-vm-modules node_modules/.bin/jest --config jest.integration.config.js --watch",
|
|
18
|
+
"test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
|
|
19
|
+
"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"
|
|
14
21
|
},
|
|
15
22
|
"keywords": [
|
|
16
23
|
"testomatio",
|
|
@@ -24,7 +31,8 @@
|
|
|
24
31
|
"license": "MIT",
|
|
25
32
|
"dependencies": {
|
|
26
33
|
"@modelcontextprotocol/sdk": "^0.4.0",
|
|
27
|
-
"commander": "^12.0.0"
|
|
34
|
+
"commander": "^12.0.0",
|
|
35
|
+
"dotenv": "^17.2.3"
|
|
28
36
|
},
|
|
29
37
|
"files": [
|
|
30
38
|
"index.js",
|
|
@@ -32,5 +40,8 @@
|
|
|
32
40
|
],
|
|
33
41
|
"engines": {
|
|
34
42
|
"node": ">=18.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"jest": "^30.2.0"
|
|
35
46
|
}
|
|
36
47
|
}
|