@testomatio/mcp 2.0.0 → 2.0.1

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 CHANGED
@@ -8,10 +8,12 @@ Model Context Protocol (MCP) server that enables AI assistants (Claude, Cursor,
8
8
  - Tests, Suites, Plans, Runs, TestRuns, RunGroups, Steps, Snippets, Labels
9
9
  - Tags (read-only access)
10
10
  - Issues (global + scoped helpers for tests/suites/runs/testruns/plans)
11
- - **Smart Search** - delegates to list endpoints with query/filter forwarding
11
+ - **Smart Search** - delegates to list endpoints with OpenAPI-aligned query/filter forwarding
12
12
  - **Issue Linking** - link/unlink issues to any resource
13
13
  - **API Compatibility** - automatic handling of payload format differences (flat vs wrapped)
14
14
  - **Run Management** - status transitions via `status_event` parameter
15
+ - **TQL-Only Search** - `tests_list/tests_search` and `runs_list/runs_search` use `tql` as the single search/filter input
16
+ - **Safe TQL Guidance** - prefer documented expressions like `priority == high`, `state == automated`, `size == 5`, `size > 1`; do not guess undocumented TQL syntax; fall back only when the API rejects the TQL expression or the needed field is not supported
15
17
 
16
18
  ## Quick Start
17
19
 
@@ -133,7 +135,7 @@ Add this config to `opencode.json` in your project root, or to `~/.config/openco
133
135
  ```json
134
136
  {
135
137
  "name": "tests_list",
136
- "arguments": { "page": 1, "per_page": 50 }
138
+ "arguments": { "page": 1, "per_page": 50, "tql": "priority == high" }
137
139
  }
138
140
  ```
139
141
 
@@ -178,16 +180,16 @@ Complete tool reference: [docs/tools.md](./docs/tools.md)
178
180
 
179
181
  ## Project Structure
180
182
 
181
- ```
183
+ ```text
182
184
  src/
183
- ├── config/ # Config loading, defaults
184
- ├── core/ # Errors, logger
185
- ├── api/ # HTTP client, Testomat.io API client
186
- ├── mcp/ # MCP server, tools, registry
187
- │ ├── definitions/ # Tool definitions by entity
188
- │ ├── configs/ # Registry generation configs
189
- │ └── registry/ # Tool handlers
190
- └── cli/ # CLI bootstrap
185
+ |- config/ # Config loading, defaults
186
+ |- core/ # Errors, logger
187
+ |- api/ # HTTP client, Testomat.io API client
188
+ |- mcp/ # MCP server, tools, registry
189
+ | |- definitions/ # Tool definitions by entity
190
+ | |- configs/ # Registry generation configs
191
+ | `- registry/ # Tool handlers
192
+ `- cli/ # CLI bootstrap
191
193
  ```
192
194
 
193
195
  ## Environment Variables
@@ -236,7 +238,10 @@ NODE_EXTRA_CA_CERTS=/path/to/company-root-ca.pem testomatio-mcp --token <TOKEN>
236
238
  ## Important Notes
237
239
 
238
240
  - **Run Status** - Use `runs_update` with `status_event` for transitions (finish, launch, rerun, etc.)
239
- - **Search** - No dedicated `/search` endpoints; search uses list with filters
241
+ - **Search** - No dedicated `/search` endpoints. MCP search tools delegate to list tools; for `tests` and `runs` the MCP interface is intentionally simplified to `tql`, while other entities stay closer to Public API v2 filters
242
+ - **TQL** - Use `tql` as the single search/filter input for `tests_list/tests_search` and `runs_list/runs_search`
243
+ - **TQL Safety** - Prefer documented expressions like `priority == high`, `state == automated`, `size == 5`, `size > 1`; do not invent tag-style or free-text syntax unless it was verified
244
+ - **Fallback Rule** - For `tests` and `runs`, try `tql` first. Use other tools or extra analysis only after the API rejects the TQL expression or the needed field is not supported by TQL
240
245
  - **Issue Linking** - Scoped helpers available: `{entity}_issues_link/unlink`
241
246
 
242
247
  ## Development
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/mcp",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Model Context Protocol server for Testomatio API",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -46,7 +46,6 @@ export const ENTITY_CRUD_CONFIGS = [
46
46
  resource: 'rungroups',
47
47
  idArg: 'rungroup_id',
48
48
  listMethod: 'listRungroups',
49
- searchMethod: 'searchRungroups',
50
49
  payloadBuilder: 'buildRungroupPayload',
51
50
  wrapperKey: 'rungroup',
52
51
  createMode: 'wrapped',
@@ -57,7 +56,6 @@ export const ENTITY_CRUD_CONFIGS = [
57
56
  resource: 'steps',
58
57
  idArg: 'step_id',
59
58
  listMethod: 'listSteps',
60
- searchMethod: 'searchSteps',
61
59
  payloadBuilder: 'buildStepPayload',
62
60
  wrapperKey: 'step',
63
61
  createMode: 'wrapped',
@@ -68,7 +66,6 @@ export const ENTITY_CRUD_CONFIGS = [
68
66
  resource: 'snippets',
69
67
  idArg: 'snippet_id',
70
68
  listMethod: 'listSnippets',
71
- searchMethod: 'searchSnippets',
72
69
  payloadBuilder: 'buildSnippetPayload',
73
70
  wrapperKey: 'snippet',
74
71
  createMode: 'wrapped',
@@ -79,7 +76,6 @@ export const ENTITY_CRUD_CONFIGS = [
79
76
  resource: 'labels',
80
77
  idArg: 'label_id',
81
78
  listMethod: 'listLabels',
82
- searchMethod: 'searchLabels',
83
79
  payloadBuilder: 'buildLabelPayload',
84
80
  wrapperKey: 'label',
85
81
  createMode: 'wrapped',
@@ -13,9 +13,6 @@ export const LABELS_TOOLS = [
13
13
  "type": "integer",
14
14
  "minimum": 1,
15
15
  "maximum": 100
16
- },
17
- "query": {
18
- "type": "string"
19
16
  }
20
17
  },
21
18
  "additionalProperties": false
@@ -148,26 +145,4 @@ export const LABELS_TOOLS = [
148
145
  "additionalProperties": false
149
146
  }
150
147
  },
151
- {
152
- "name": "labels_search",
153
- "description": "Search labels (delegates to labels list; docs has no dedicated search parameter)",
154
- "inputSchema": {
155
- "type": "object",
156
- "properties": {
157
- "query": {
158
- "type": "string"
159
- },
160
- "page": {
161
- "type": "integer",
162
- "minimum": 1
163
- },
164
- "per_page": {
165
- "type": "integer",
166
- "minimum": 1,
167
- "maximum": 100
168
- }
169
- },
170
- "additionalProperties": false
171
- }
172
- }
173
148
  ];
@@ -14,7 +14,24 @@ export const PLANS_TOOLS = [
14
14
  "minimum": 1,
15
15
  "maximum": 100
16
16
  },
17
- "query": {
17
+ "kind": {
18
+ "type": "string",
19
+ "enum": [
20
+ "manual",
21
+ "automated",
22
+ "mixed"
23
+ ]
24
+ },
25
+ "hidden": {
26
+ "type": "boolean"
27
+ },
28
+ "labels": {
29
+ "type": "array",
30
+ "items": {
31
+ "type": "string"
32
+ }
33
+ },
34
+ "search_text": {
18
35
  "type": "string"
19
36
  }
20
37
  },
@@ -95,6 +112,7 @@ export const PLANS_TOOLS = [
95
112
  "label",
96
113
  "custom_field",
97
114
  "tag",
115
+ "milestone",
98
116
  "issue",
99
117
  "jira"
100
118
  ]
@@ -179,6 +197,7 @@ export const PLANS_TOOLS = [
179
197
  "label",
180
198
  "custom_field",
181
199
  "tag",
200
+ "milestone",
182
201
  "issue",
183
202
  "jira"
184
203
  ]
@@ -224,7 +243,7 @@ export const PLANS_TOOLS = [
224
243
  "inputSchema": {
225
244
  "type": "object",
226
245
  "properties": {
227
- "query": {
246
+ "search_text": {
228
247
  "type": "string"
229
248
  },
230
249
  "page": {
@@ -235,6 +254,23 @@ export const PLANS_TOOLS = [
235
254
  "type": "integer",
236
255
  "minimum": 1,
237
256
  "maximum": 100
257
+ },
258
+ "kind": {
259
+ "type": "string",
260
+ "enum": [
261
+ "manual",
262
+ "automated",
263
+ "mixed"
264
+ ]
265
+ },
266
+ "hidden": {
267
+ "type": "boolean"
268
+ },
269
+ "labels": {
270
+ "type": "array",
271
+ "items": {
272
+ "type": "string"
273
+ }
238
274
  }
239
275
  },
240
276
  "additionalProperties": false
@@ -13,9 +13,6 @@ export const RUNGROUPS_TOOLS = [
13
13
  "type": "integer",
14
14
  "minimum": 1,
15
15
  "maximum": 100
16
- },
17
- "query": {
18
- "type": "string"
19
16
  }
20
17
  },
21
18
  "additionalProperties": false
@@ -130,26 +127,4 @@ export const RUNGROUPS_TOOLS = [
130
127
  "additionalProperties": false
131
128
  }
132
129
  },
133
- {
134
- "name": "rungroups_search",
135
- "description": "Search run groups (delegates to rungroups list; docs has no dedicated search parameter)",
136
- "inputSchema": {
137
- "type": "object",
138
- "properties": {
139
- "query": {
140
- "type": "string"
141
- },
142
- "page": {
143
- "type": "integer",
144
- "minimum": 1
145
- },
146
- "per_page": {
147
- "type": "integer",
148
- "minimum": 1,
149
- "maximum": 100
150
- }
151
- },
152
- "additionalProperties": false
153
- }
154
- }
155
130
  ];
@@ -1,7 +1,7 @@
1
1
  export const RUNS_TOOLS = [
2
2
  {
3
3
  "name": "runs_list",
4
- "description": "List runs (/api/v2/{project_id}/runs)",
4
+ "description": "List runs (/api/v2/{project_id}/runs). Use `tql` first for search/filtering. Prefer known-safe expressions such as `size == 5` or `size > 1`. Do not guess undocumented TQL syntax. Fall back to other tools or analysis only if the API rejects the TQL expression or the needed field is not supported by TQL.",
5
5
  "inputSchema": {
6
6
  "type": "object",
7
7
  "properties": {
@@ -14,8 +14,9 @@ export const RUNS_TOOLS = [
14
14
  "minimum": 1,
15
15
  "maximum": 100
16
16
  },
17
- "query": {
18
- "type": "string"
17
+ "tql": {
18
+ "type": "string",
19
+ "description": "Universal TQL filter for runs. Prefer known-safe expressions like `size == 5` or `size > 1`. Do not guess undocumented syntax. Fall back only if the API rejects the TQL expression or the needed field is not supported by TQL."
19
20
  }
20
21
  },
21
22
  "additionalProperties": false
@@ -49,8 +50,11 @@ export const RUNS_TOOLS = [
49
50
  "description": {
50
51
  "type": "string"
51
52
  },
52
- "plan_id": {
53
- "type": "string"
53
+ "plan_ids": {
54
+ "type": "array",
55
+ "items": {
56
+ "type": "string"
57
+ }
54
58
  },
55
59
  "kind": {
56
60
  "type": "string",
@@ -95,9 +99,6 @@ export const RUNS_TOOLS = [
95
99
  "type": "string"
96
100
  }
97
101
  },
98
- "configuration": {
99
- "type": "object"
100
- },
101
102
  "link": {
102
103
  "type": "array",
103
104
  "items": {
@@ -116,6 +117,7 @@ export const RUNS_TOOLS = [
116
117
  "label",
117
118
  "custom_field",
118
119
  "tag",
120
+ "milestone",
119
121
  "issue",
120
122
  "jira"
121
123
  ]
@@ -154,9 +156,6 @@ export const RUNS_TOOLS = [
154
156
  "description": {
155
157
  "type": "string"
156
158
  },
157
- "plan_id": {
158
- "type": "string"
159
- },
160
159
  "kind": {
161
160
  "type": "string",
162
161
  "enum": [
@@ -205,9 +204,6 @@ export const RUNS_TOOLS = [
205
204
  "type": "string"
206
205
  }
207
206
  },
208
- "configuration": {
209
- "type": "object"
210
- },
211
207
  "link": {
212
208
  "type": "array",
213
209
  "items": {
@@ -226,6 +222,7 @@ export const RUNS_TOOLS = [
226
222
  "label",
227
223
  "custom_field",
228
224
  "tag",
225
+ "milestone",
229
226
  "issue",
230
227
  "jira"
231
228
  ]
@@ -267,13 +264,10 @@ export const RUNS_TOOLS = [
267
264
  },
268
265
  {
269
266
  "name": "runs_search",
270
- "description": "Search runs (delegates to runs list; docs has no dedicated search parameter)",
267
+ "description": "Search runs using TQL (delegates to runs_list). Use `tql` first. Prefer known-safe expressions such as `size == 5` or `size > 1`. Do not guess undocumented TQL syntax. Fall back to other tools or analysis only if the API rejects the TQL expression or the needed field is not supported by TQL.",
271
268
  "inputSchema": {
272
269
  "type": "object",
273
270
  "properties": {
274
- "query": {
275
- "type": "string"
276
- },
277
271
  "page": {
278
272
  "type": "integer",
279
273
  "minimum": 1
@@ -282,6 +276,10 @@ export const RUNS_TOOLS = [
282
276
  "type": "integer",
283
277
  "minimum": 1,
284
278
  "maximum": 100
279
+ },
280
+ "tql": {
281
+ "type": "string",
282
+ "description": "Universal TQL filter for runs. Prefer known-safe expressions like `size == 5` or `size > 1`. Do not guess undocumented syntax. Fall back only if the API rejects the TQL expression or the needed field is not supported by TQL."
285
283
  }
286
284
  },
287
285
  "additionalProperties": false
@@ -13,9 +13,6 @@ export const SNIPPETS_TOOLS = [
13
13
  "type": "integer",
14
14
  "minimum": 1,
15
15
  "maximum": 100
16
- },
17
- "query": {
18
- "type": "string"
19
16
  }
20
17
  },
21
18
  "additionalProperties": false
@@ -67,6 +64,7 @@ export const SNIPPETS_TOOLS = [
67
64
  "label",
68
65
  "custom_field",
69
66
  "tag",
67
+ "milestone",
70
68
  "issue",
71
69
  "jira"
72
70
  ]
@@ -123,6 +121,7 @@ export const SNIPPETS_TOOLS = [
123
121
  "label",
124
122
  "custom_field",
125
123
  "tag",
124
+ "milestone",
126
125
  "issue",
127
126
  "jira"
128
127
  ]
@@ -162,26 +161,4 @@ export const SNIPPETS_TOOLS = [
162
161
  "additionalProperties": false
163
162
  }
164
163
  },
165
- {
166
- "name": "snippets_search",
167
- "description": "Search snippets (delegates to snippets list; docs has no dedicated search parameter)",
168
- "inputSchema": {
169
- "type": "object",
170
- "properties": {
171
- "query": {
172
- "type": "string"
173
- },
174
- "page": {
175
- "type": "integer",
176
- "minimum": 1
177
- },
178
- "per_page": {
179
- "type": "integer",
180
- "minimum": 1,
181
- "maximum": 100
182
- }
183
- },
184
- "additionalProperties": false
185
- }
186
- }
187
164
  ];
@@ -13,9 +13,6 @@ export const STEPS_TOOLS = [
13
13
  "type": "integer",
14
14
  "minimum": 1,
15
15
  "maximum": 100
16
- },
17
- "query": {
18
- "type": "string"
19
16
  }
20
17
  },
21
18
  "additionalProperties": false
@@ -67,6 +64,7 @@ export const STEPS_TOOLS = [
67
64
  "label",
68
65
  "custom_field",
69
66
  "tag",
67
+ "milestone",
70
68
  "issue",
71
69
  "jira"
72
70
  ]
@@ -123,6 +121,7 @@ export const STEPS_TOOLS = [
123
121
  "label",
124
122
  "custom_field",
125
123
  "tag",
124
+ "milestone",
126
125
  "issue",
127
126
  "jira"
128
127
  ]
@@ -162,26 +161,4 @@ export const STEPS_TOOLS = [
162
161
  "additionalProperties": false
163
162
  }
164
163
  },
165
- {
166
- "name": "steps_search",
167
- "description": "Search steps (delegates to steps list; docs has no dedicated search parameter)",
168
- "inputSchema": {
169
- "type": "object",
170
- "properties": {
171
- "query": {
172
- "type": "string"
173
- },
174
- "page": {
175
- "type": "integer",
176
- "minimum": 1
177
- },
178
- "per_page": {
179
- "type": "integer",
180
- "minimum": 1,
181
- "maximum": 100
182
- }
183
- },
184
- "additionalProperties": false
185
- }
186
- }
187
164
  ];
@@ -29,9 +29,6 @@ export const SUITES_TOOLS = [
29
29
  },
30
30
  "search_text": {
31
31
  "type": "string"
32
- },
33
- "query": {
34
- "type": "string"
35
32
  }
36
33
  },
37
34
  "additionalProperties": false
@@ -101,8 +98,10 @@ export const SUITES_TOOLS = [
101
98
  "label",
102
99
  "custom_field",
103
100
  "tag",
101
+ "milestone",
104
102
  "issue",
105
- "jira"
103
+ "jira",
104
+ "requirement"
106
105
  ]
107
106
  },
108
107
  "value": {
@@ -175,8 +174,10 @@ export const SUITES_TOOLS = [
175
174
  "label",
176
175
  "custom_field",
177
176
  "tag",
177
+ "milestone",
178
178
  "issue",
179
- "jira"
179
+ "jira",
180
+ "requirement"
180
181
  ]
181
182
  },
182
183
  "value": {
@@ -223,9 +224,6 @@ export const SUITES_TOOLS = [
223
224
  "search_text": {
224
225
  "type": "string"
225
226
  },
226
- "query": {
227
- "type": "string"
228
- },
229
227
  "page": {
230
228
  "type": "integer",
231
229
  "minimum": 1
@@ -17,8 +17,104 @@ export const TESTRUNS_TOOLS = [
17
17
  "run_id": {
18
18
  "type": "string"
19
19
  },
20
- "query": {
20
+ "test_ids": {
21
+ "type": [
22
+ "array",
23
+ "string"
24
+ ],
25
+ "items": {
26
+ "type": "string"
27
+ }
28
+ },
29
+ "filter_status": {
30
+ "type": "string",
31
+ "enum": [
32
+ "passed",
33
+ "failed",
34
+ "skipped",
35
+ "pending"
36
+ ]
37
+ },
38
+ "filter_kind": {
39
+ "type": "string",
40
+ "enum": [
41
+ "manual",
42
+ "automated"
43
+ ]
44
+ },
45
+ "filter_user": {
46
+ "type": [
47
+ "integer",
48
+ "string"
49
+ ]
50
+ },
51
+ "filter_priority": {
52
+ "type": "string",
53
+ "enum": [
54
+ "low",
55
+ "normal",
56
+ "important",
57
+ "high",
58
+ "critical"
59
+ ]
60
+ },
61
+ "filter_substatus": {
21
62
  "type": "string"
63
+ },
64
+ "filter_search": {
65
+ "type": "string"
66
+ },
67
+ "filter_message": {
68
+ "type": "boolean"
69
+ },
70
+ "filter_link": {
71
+ "type": "boolean"
72
+ },
73
+ "filter_finished_at_date_range": {
74
+ "type": "string"
75
+ },
76
+ "tags": {
77
+ "type": [
78
+ "array",
79
+ "string"
80
+ ],
81
+ "items": {
82
+ "type": "string"
83
+ }
84
+ },
85
+ "labels": {
86
+ "type": [
87
+ "array",
88
+ "string"
89
+ ],
90
+ "items": {
91
+ "type": "string"
92
+ }
93
+ },
94
+ "envs": {
95
+ "type": [
96
+ "array",
97
+ "string"
98
+ ],
99
+ "items": {
100
+ "type": "string"
101
+ }
102
+ },
103
+ "rungroups": {
104
+ "type": [
105
+ "array",
106
+ "string"
107
+ ],
108
+ "items": {
109
+ "type": "string"
110
+ }
111
+ },
112
+ "defects": {
113
+ "type": "string",
114
+ "enum": [
115
+ "has_defects",
116
+ "without_defects"
117
+ ]
22
118
  }
23
119
  },
24
120
  "additionalProperties": false
@@ -142,7 +238,51 @@ export const TESTRUNS_TOOLS = [
142
238
  "run_id": {
143
239
  "type": "string"
144
240
  },
145
- "query": {
241
+ "test_ids": {
242
+ "type": [
243
+ "array",
244
+ "string"
245
+ ],
246
+ "items": {
247
+ "type": "string"
248
+ }
249
+ },
250
+ "filter_status": {
251
+ "type": "string",
252
+ "enum": [
253
+ "passed",
254
+ "failed",
255
+ "skipped",
256
+ "pending"
257
+ ]
258
+ },
259
+ "filter_kind": {
260
+ "type": "string",
261
+ "enum": [
262
+ "manual",
263
+ "automated"
264
+ ]
265
+ },
266
+ "filter_user": {
267
+ "type": [
268
+ "integer",
269
+ "string"
270
+ ]
271
+ },
272
+ "filter_priority": {
273
+ "type": "string",
274
+ "enum": [
275
+ "low",
276
+ "normal",
277
+ "important",
278
+ "high",
279
+ "critical"
280
+ ]
281
+ },
282
+ "filter_substatus": {
283
+ "type": "string"
284
+ },
285
+ "filter_search": {
146
286
  "type": "string"
147
287
  },
148
288
  "page": {
@@ -153,6 +293,58 @@ export const TESTRUNS_TOOLS = [
153
293
  "type": "integer",
154
294
  "minimum": 1,
155
295
  "maximum": 100
296
+ },
297
+ "filter_message": {
298
+ "type": "boolean"
299
+ },
300
+ "filter_link": {
301
+ "type": "boolean"
302
+ },
303
+ "filter_finished_at_date_range": {
304
+ "type": "string"
305
+ },
306
+ "tags": {
307
+ "type": [
308
+ "array",
309
+ "string"
310
+ ],
311
+ "items": {
312
+ "type": "string"
313
+ }
314
+ },
315
+ "labels": {
316
+ "type": [
317
+ "array",
318
+ "string"
319
+ ],
320
+ "items": {
321
+ "type": "string"
322
+ }
323
+ },
324
+ "envs": {
325
+ "type": [
326
+ "array",
327
+ "string"
328
+ ],
329
+ "items": {
330
+ "type": "string"
331
+ }
332
+ },
333
+ "rungroups": {
334
+ "type": [
335
+ "array",
336
+ "string"
337
+ ],
338
+ "items": {
339
+ "type": "string"
340
+ }
341
+ },
342
+ "defects": {
343
+ "type": "string",
344
+ "enum": [
345
+ "has_defects",
346
+ "without_defects"
347
+ ]
156
348
  }
157
349
  },
158
350
  "additionalProperties": false
@@ -1,7 +1,7 @@
1
1
  export const TESTS_TOOLS = [
2
2
  {
3
3
  "name": "tests_list",
4
- "description": "List tests (/api/v2/{project_id}/tests)",
4
+ "description": "List tests (/api/v2/{project_id}/tests). Use `tql` first for search/filtering. Prefer known-safe expressions such as `priority == high` or `state == automated`. Do not guess undocumented TQL syntax. Fall back to other tools or analysis only if the API rejects the TQL expression or the needed field is not supported by TQL.",
5
5
  "inputSchema": {
6
6
  "type": "object",
7
7
  "properties": {
@@ -14,23 +14,9 @@ export const TESTS_TOOLS = [
14
14
  "minimum": 1,
15
15
  "maximum": 100
16
16
  },
17
- "suite_id": {
18
- "type": "string"
19
- },
20
- "search_text": {
21
- "type": "string"
22
- },
23
- "query": {
24
- "type": "string"
25
- },
26
- "assigned_to": {
27
- "type": "string"
28
- },
29
- "priority": {
30
- "type": "string"
31
- },
32
- "state": {
33
- "type": "string"
17
+ "tql": {
18
+ "type": "string",
19
+ "description": "Universal TQL filter for tests. Prefer known-safe expressions like `priority == high` or `state == automated`. Do not guess undocumented syntax. Fall back only if the API rejects the TQL expression or the needed field is not supported by TQL."
34
20
  }
35
21
  },
36
22
  "additionalProperties": false
@@ -100,6 +86,7 @@ export const TESTS_TOOLS = [
100
86
  "label",
101
87
  "custom_field",
102
88
  "tag",
89
+ "milestone",
103
90
  "issue",
104
91
  "jira"
105
92
  ]
@@ -178,6 +165,7 @@ export const TESTS_TOOLS = [
178
165
  "label",
179
166
  "custom_field",
180
167
  "tag",
168
+ "milestone",
181
169
  "issue",
182
170
  "jira"
183
171
  ]
@@ -219,16 +207,10 @@ export const TESTS_TOOLS = [
219
207
  },
220
208
  {
221
209
  "name": "tests_search",
222
- "description": "Search tests by text (delegates to tests list with search_text)",
210
+ "description": "Search tests using TQL (delegates to tests_list). Use `tql` first. Prefer known-safe expressions such as `priority == high` or `state == automated`. Do not guess undocumented TQL syntax. Fall back to other tools or analysis only if the API rejects the TQL expression or the needed field is not supported by TQL.",
223
211
  "inputSchema": {
224
212
  "type": "object",
225
213
  "properties": {
226
- "query": {
227
- "type": "string"
228
- },
229
- "search_text": {
230
- "type": "string"
231
- },
232
214
  "page": {
233
215
  "type": "integer",
234
216
  "minimum": 1
@@ -238,17 +220,9 @@ export const TESTS_TOOLS = [
238
220
  "minimum": 1,
239
221
  "maximum": 100
240
222
  },
241
- "suite_id": {
242
- "type": "string"
243
- },
244
- "assigned_to": {
245
- "type": "string"
246
- },
247
- "priority": {
248
- "type": "string"
249
- },
250
- "state": {
251
- "type": "string"
223
+ "tql": {
224
+ "type": "string",
225
+ "description": "Universal TQL filter for tests. Prefer known-safe expressions like `priority == high` or `state == automated`. Do not guess undocumented syntax. Fall back only if the API rejects the TQL expression or the needed field is not supported by TQL."
252
226
  }
253
227
  },
254
228
  "additionalProperties": false
@@ -8,8 +8,10 @@ export const handlerMethods = {
8
8
 
9
9
  handlers[`${toolPrefix}_list`] = async (args = {}) =>
10
10
  this.asText(await this[listMethod](args));
11
- handlers[`${toolPrefix}_search`] = async (args = {}) =>
12
- this.asText(await this[searchMethod](args));
11
+ if (searchMethod) {
12
+ handlers[`${toolPrefix}_search`] = async (args = {}) =>
13
+ this.asText(await this[searchMethod](args));
14
+ }
13
15
  handlers[`${toolPrefix}_get`] = async (args = {}) =>
14
16
  this.asText(await this.apiClient.get(resource, this.pickRequiredArg(args, idArg)));
15
17
  handlers[`${toolPrefix}_create`] = async (args = {}) =>
@@ -1,93 +1,139 @@
1
1
  export const listingMethods = {
2
- listTests({ page, per_page: perPage, suite_id: suiteId, search_text: searchText, query, ...filters } = {}) {
2
+ listTests({
3
+ page,
4
+ per_page: perPage,
5
+ tql,
6
+ } = {}) {
3
7
  return this.apiClient.list('tests', {
4
8
  page,
5
9
  per_page: perPage,
6
- suite_id: suiteId,
7
- search_text: searchText || query,
8
- ...filters,
10
+ tql,
9
11
  });
10
12
  },
11
13
 
12
- searchTests({ query, search_text: searchText, ...rest } = {}) {
14
+ searchTests({ page, per_page: perPage, tql } = {}) {
13
15
  return this.listTests({
14
- ...rest,
15
- search_text: searchText || query,
16
+ page,
17
+ per_page: perPage,
18
+ tql,
16
19
  });
17
20
  },
18
21
 
19
- listSuites({ page, per_page: perPage, file_type: fileType, tag, labels, search_text: searchText, query } = {}) {
22
+ listSuites({ page, per_page: perPage, file_type: fileType, tag, labels, search_text: searchText } = {}) {
20
23
  return this.apiClient.list('suites', {
21
24
  page,
22
25
  per_page: perPage,
23
26
  file_type: fileType,
24
27
  tag,
25
28
  labels,
26
- search_text: searchText || query,
29
+ search_text: searchText,
27
30
  });
28
31
  },
29
32
 
30
- searchSuites({ query, search_text: searchText, ...rest } = {}) {
33
+ searchSuites({ search_text: searchText, ...rest } = {}) {
31
34
  return this.listSuites({
32
35
  ...rest,
33
- search_text: searchText || query,
36
+ search_text: searchText,
34
37
  });
35
38
  },
36
39
 
37
- listRuns({ page, per_page: perPage, query } = {}) {
38
- return this.apiClient.list('runs', { page, per_page: perPage, query });
39
- },
40
-
41
- searchRuns({ page, per_page: perPage, query } = {}) {
42
- return this.listRuns({ page, per_page: perPage, query });
43
- },
44
-
45
- listTestruns({ page, per_page: perPage, run_id: runId, query } = {}) {
46
- return this.apiClient.list('testruns', { page, per_page: perPage, run_id: runId, query });
47
- },
48
-
49
- searchTestruns({ page, per_page: perPage, run_id: runId, query } = {}) {
50
- return this.listTestruns({ page, per_page: perPage, run_id: runId, query });
51
- },
52
-
53
- listRungroups({ page, per_page: perPage, query } = {}) {
54
- return this.apiClient.list('rungroups', { page, per_page: perPage, query });
55
- },
56
-
57
- searchRungroups({ page, per_page: perPage, query } = {}) {
58
- return this.listRungroups({ page, per_page: perPage, query });
40
+ listRuns({
41
+ page,
42
+ per_page: perPage,
43
+ tql,
44
+ } = {}) {
45
+ return this.apiClient.list('runs', {
46
+ page,
47
+ per_page: perPage,
48
+ tql,
49
+ });
59
50
  },
60
51
 
61
- listSteps({ page, per_page: perPage, query } = {}) {
62
- return this.apiClient.list('steps', { page, per_page: perPage, query });
52
+ searchRuns({ page, per_page: perPage, tql } = {}) {
53
+ return this.listRuns({ page, per_page: perPage, tql });
54
+ },
55
+
56
+ listTestruns({
57
+ page,
58
+ per_page: perPage,
59
+ run_id: runId,
60
+ test_ids: testIds,
61
+ filter_status: filterStatus,
62
+ filter_kind: filterKind,
63
+ filter_user: filterUser,
64
+ filter_priority: filterPriority,
65
+ filter_substatus: filterSubstatus,
66
+ filter_search: filterSearch,
67
+ filter_message: filterMessage,
68
+ filter_link: filterLink,
69
+ filter_finished_at_date_range: filterFinishedAtDateRange,
70
+ tags,
71
+ labels,
72
+ envs,
73
+ rungroups,
74
+ defects,
75
+ } = {}) {
76
+ return this.apiClient.list('testruns', {
77
+ page,
78
+ per_page: perPage,
79
+ run_id: runId,
80
+ test_ids: Array.isArray(testIds) ? testIds.join(',') : testIds,
81
+ 'filter[status]': filterStatus,
82
+ 'filter[kind]': filterKind,
83
+ 'filter[user]': filterUser,
84
+ 'filter[priority]': filterPriority,
85
+ 'filter[substatus]': filterSubstatus,
86
+ 'filter[search]': filterSearch,
87
+ 'filter[message]': filterMessage,
88
+ 'filter[link]': filterLink,
89
+ 'filter[finished_at_date_range]': filterFinishedAtDateRange,
90
+ tags: Array.isArray(tags) ? tags.join(',') : tags,
91
+ labels: Array.isArray(labels) ? labels.join(',') : labels,
92
+ envs: Array.isArray(envs) ? envs.join(',') : envs,
93
+ rungroups: Array.isArray(rungroups) ? rungroups.join(',') : rungroups,
94
+ defects,
95
+ });
63
96
  },
64
97
 
65
- searchSteps({ page, per_page: perPage, query } = {}) {
66
- return this.listSteps({ page, per_page: perPage, query });
98
+ searchTestruns({ page, per_page: perPage, run_id: runId, filter_search: filterSearch, ...rest } = {}) {
99
+ return this.listTestruns({
100
+ page,
101
+ per_page: perPage,
102
+ run_id: runId,
103
+ filter_search: filterSearch,
104
+ ...rest,
105
+ });
67
106
  },
68
107
 
69
- listSnippets({ page, per_page: perPage, query } = {}) {
70
- return this.apiClient.list('snippets', { page, per_page: perPage, query });
108
+ listRungroups({ page, per_page: perPage } = {}) {
109
+ return this.apiClient.list('rungroups', { page, per_page: perPage });
71
110
  },
72
111
 
73
- searchSnippets({ page, per_page: perPage, query } = {}) {
74
- return this.listSnippets({ page, per_page: perPage, query });
112
+ listSteps({ page, per_page: perPage } = {}) {
113
+ return this.apiClient.list('steps', { page, per_page: perPage });
75
114
  },
76
115
 
77
- listLabels({ page, per_page: perPage, query } = {}) {
78
- return this.apiClient.list('labels', { page, per_page: perPage, query });
116
+ listSnippets({ page, per_page: perPage } = {}) {
117
+ return this.apiClient.list('snippets', { page, per_page: perPage });
79
118
  },
80
119
 
81
- searchLabels({ page, per_page: perPage, query } = {}) {
82
- return this.listLabels({ page, per_page: perPage, query });
120
+ listLabels({ page, per_page: perPage } = {}) {
121
+ return this.apiClient.list('labels', { page, per_page: perPage });
83
122
  },
84
123
 
85
- listPlans({ page, per_page: perPage, query } = {}) {
86
- return this.apiClient.list('plans', { page, per_page: perPage, query });
124
+ listPlans({ page, per_page: perPage, kind, hidden, labels, search_text: searchText } = {}) {
125
+ return this.apiClient.list('plans', {
126
+ page,
127
+ per_page: perPage,
128
+ kind,
129
+ hidden,
130
+ 'labels[]': labels,
131
+ search_text: searchText,
132
+ });
87
133
  },
88
134
 
89
- searchPlans({ page, per_page: perPage, query } = {}) {
90
- return this.listPlans({ page, per_page: perPage, query });
135
+ searchPlans({ page, per_page: perPage, search_text: searchText, ...rest } = {}) {
136
+ return this.listPlans({ page, per_page: perPage, search_text: searchText, ...rest });
91
137
  },
92
138
 
93
139
  listTags() {
@@ -76,7 +76,7 @@ export const payloadMethods = {
76
76
  buildRunCreatePayload({
77
77
  title,
78
78
  description,
79
- plan_id: planId,
79
+ plan_ids: planIds,
80
80
  kind,
81
81
  rungroup_id: rungroupId,
82
82
  env,
@@ -85,13 +85,12 @@ export const payloadMethods = {
85
85
  test_ids: testIds,
86
86
  suite_ids: suiteIds,
87
87
  envs,
88
- configuration,
89
88
  link,
90
89
  } = {}) {
91
90
  return {
92
91
  title,
93
92
  description,
94
- plan_id: planId,
93
+ plan_ids: planIds,
95
94
  kind,
96
95
  rungroup_id: rungroupId,
97
96
  env,
@@ -100,7 +99,6 @@ export const payloadMethods = {
100
99
  test_ids: testIds,
101
100
  suite_ids: suiteIds,
102
101
  envs,
103
- configuration,
104
102
  link,
105
103
  };
106
104
  },
@@ -108,7 +106,6 @@ export const payloadMethods = {
108
106
  buildRunUpdatePayload({
109
107
  title,
110
108
  description,
111
- plan_id: planId,
112
109
  kind,
113
110
  rungroup_id: rungroupId,
114
111
  env,
@@ -117,13 +114,11 @@ export const payloadMethods = {
117
114
  assign_strategy: assignStrategy,
118
115
  test_ids: testIds,
119
116
  suite_ids: suiteIds,
120
- configuration,
121
117
  link,
122
118
  } = {}) {
123
119
  return {
124
120
  title,
125
121
  description,
126
- plan_id: planId,
127
122
  kind,
128
123
  rungroup_id: rungroupId,
129
124
  env,
@@ -132,7 +127,6 @@ export const payloadMethods = {
132
127
  assign_strategy: assignStrategy,
133
128
  test_ids: testIds,
134
129
  suite_ids: suiteIds,
135
- configuration,
136
130
  link,
137
131
  };
138
132
  },