@testomatio/mcp 2.2.0-beta.2 → 2.2.0-beta.3
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/package.json +1 -1
- package/src/api/testomatio-client.js +8 -8
- package/src/mcp/configs/entity-crud-config.js +13 -0
- package/src/mcp/definitions/branches.js +110 -0
- package/src/mcp/definitions/runs.js +11 -5
- package/src/mcp/definitions/suites.js +11 -5
- package/src/mcp/definitions/tests.js +11 -5
- package/src/mcp/registry/handlers.js +53 -19
- package/src/mcp/registry/listings.js +26 -1
- package/src/mcp/registry/payloads.js +27 -21
- package/src/mcp/tool-definitions.js +2 -0
- package/src/mcp/tool-registry.js +5 -2
package/package.json
CHANGED
|
@@ -27,8 +27,8 @@ export class TestomatioApiClient {
|
|
|
27
27
|
return this.http.request('GET', this.buildPath(resource, id), { query });
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
async create(resource, body = {}) {
|
|
31
|
-
return this.mutate('POST', this.buildPath(resource), { body });
|
|
30
|
+
async create(resource, body = {}, query = {}) {
|
|
31
|
+
return this.mutate('POST', this.buildPath(resource), { query, body });
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
async createMultipart(resource, formData, query = {}) {
|
|
@@ -39,16 +39,16 @@ export class TestomatioApiClient {
|
|
|
39
39
|
return this.mutate('POST', this.buildPath(resource), { query, body });
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
async update(resource, id, body = {}) {
|
|
43
|
-
return this.mutate('PUT', this.buildPath(resource, id), { body });
|
|
42
|
+
async update(resource, id, body = {}, query = {}) {
|
|
43
|
+
return this.mutate('PUT', this.buildPath(resource, id), { query, body });
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
async patch(resource, id, body = {}) {
|
|
47
|
-
return this.mutate('PATCH', this.buildPath(resource, id), { body });
|
|
46
|
+
async patch(resource, id, body = {}, query = {}) {
|
|
47
|
+
return this.mutate('PATCH', this.buildPath(resource, id), { query, body });
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
async patchMultipart(resource, id, formData) {
|
|
51
|
-
return this.mutate('PATCH', this.buildPath(resource, id), { body: formData });
|
|
50
|
+
async patchMultipart(resource, id, formData, query = {}) {
|
|
51
|
+
return this.mutate('PATCH', this.buildPath(resource, id), { query, body: formData });
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
async delete(resource, id, query = {}) {
|
|
@@ -8,6 +8,7 @@ export const ENTITY_CRUD_CONFIGS = [
|
|
|
8
8
|
wrapperKey: 'test',
|
|
9
9
|
createMode: 'wrapped',
|
|
10
10
|
updateMode: 'wrapped',
|
|
11
|
+
queryArgs: ['branch'],
|
|
11
12
|
},
|
|
12
13
|
{
|
|
13
14
|
toolPrefix: 'suites',
|
|
@@ -18,6 +19,7 @@ export const ENTITY_CRUD_CONFIGS = [
|
|
|
18
19
|
wrapperKey: 'suite',
|
|
19
20
|
createMode: 'wrapped',
|
|
20
21
|
updateMode: 'wrapped',
|
|
22
|
+
queryArgs: ['branch'],
|
|
21
23
|
},
|
|
22
24
|
{
|
|
23
25
|
toolPrefix: 'runs',
|
|
@@ -26,6 +28,7 @@ export const ENTITY_CRUD_CONFIGS = [
|
|
|
26
28
|
listMethod: 'listRuns',
|
|
27
29
|
createMode: 'run',
|
|
28
30
|
updateMode: 'run',
|
|
31
|
+
queryArgs: ['branch'],
|
|
29
32
|
},
|
|
30
33
|
{
|
|
31
34
|
toolPrefix: 'testruns',
|
|
@@ -98,4 +101,14 @@ export const ENTITY_CRUD_CONFIGS = [
|
|
|
98
101
|
updateMode: 'requirement',
|
|
99
102
|
updateMethod: 'patch',
|
|
100
103
|
},
|
|
104
|
+
{
|
|
105
|
+
toolPrefix: 'branches',
|
|
106
|
+
resource: 'branches',
|
|
107
|
+
idArg: 'branch_id',
|
|
108
|
+
listMethod: 'listBranches',
|
|
109
|
+
payloadBuilder: 'buildBranchPayload',
|
|
110
|
+
wrapperKey: 'branch',
|
|
111
|
+
createMode: 'wrapped',
|
|
112
|
+
updateMode: 'wrapped',
|
|
113
|
+
},
|
|
101
114
|
];
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export const BRANCH_PARAM = {
|
|
2
|
+
"type": "string",
|
|
3
|
+
"description": "Branch slug to scope the request to (omit or main for the main branch). For tests and suites, a branch-local record is used when it exists, falling back to main; updating/deleting a main-only record forks an isolated copy into the branch. Runs are tagged with the branch (only reachable with the same branch afterwards). Requires the branches feature."
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const BRANCHES_TOOLS = [
|
|
7
|
+
{
|
|
8
|
+
"name": "branches_list",
|
|
9
|
+
"description":
|
|
10
|
+
'List project branches (/api/v2/{project_id}/branches). Requires the branches feature (enterprise plan). Use filter[state] / filter[title] to narrow down.',
|
|
11
|
+
"inputSchema": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"page": {
|
|
15
|
+
"type": "integer",
|
|
16
|
+
"minimum": 1
|
|
17
|
+
},
|
|
18
|
+
"per_page": {
|
|
19
|
+
"type": "integer",
|
|
20
|
+
"minimum": 1,
|
|
21
|
+
"maximum": 100
|
|
22
|
+
},
|
|
23
|
+
"filter_state": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"enum": [
|
|
26
|
+
"active",
|
|
27
|
+
"merged"
|
|
28
|
+
],
|
|
29
|
+
"description": "Filter by branch state"
|
|
30
|
+
},
|
|
31
|
+
"filter_title": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "Filter by title (partial substring match)"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"additionalProperties": false
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "branches_get",
|
|
41
|
+
"description": "Get branch by slug",
|
|
42
|
+
"inputSchema": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"branch_id": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Branch slug"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"required": [
|
|
51
|
+
"branch_id"
|
|
52
|
+
],
|
|
53
|
+
"additionalProperties": false
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "branches_create",
|
|
58
|
+
"description": "Create branch (/api/v2/{project_id}/branches)",
|
|
59
|
+
"inputSchema": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"properties": {
|
|
62
|
+
"title": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "Branch title; a slug is generated from it"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"required": [
|
|
68
|
+
"title"
|
|
69
|
+
],
|
|
70
|
+
"additionalProperties": false
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "branches_update",
|
|
75
|
+
"description": "Update branch title (/api/v2/{project_id}/branches/{slug})",
|
|
76
|
+
"inputSchema": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"properties": {
|
|
79
|
+
"branch_id": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"description": "Branch slug"
|
|
82
|
+
},
|
|
83
|
+
"title": {
|
|
84
|
+
"type": "string"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"required": [
|
|
88
|
+
"branch_id"
|
|
89
|
+
],
|
|
90
|
+
"additionalProperties": false
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "branches_delete",
|
|
95
|
+
"description": "Delete branch by slug",
|
|
96
|
+
"inputSchema": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"properties": {
|
|
99
|
+
"branch_id": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"description": "Branch slug"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"required": [
|
|
105
|
+
"branch_id"
|
|
106
|
+
],
|
|
107
|
+
"additionalProperties": false
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RUNS_TQL_INPUT_DESCRIPTION, RUNS_TQL_REFERENCE } from './tql-reference.js';
|
|
2
|
+
import { BRANCH_PARAM } from './branches.js';
|
|
2
3
|
|
|
3
4
|
export const RUNS_TOOLS = [
|
|
4
5
|
{
|
|
@@ -19,7 +20,8 @@ export const RUNS_TOOLS = [
|
|
|
19
20
|
"tql": {
|
|
20
21
|
"type": "string",
|
|
21
22
|
"description": RUNS_TQL_INPUT_DESCRIPTION
|
|
22
|
-
}
|
|
23
|
+
},
|
|
24
|
+
"branch": BRANCH_PARAM
|
|
23
25
|
},
|
|
24
26
|
"additionalProperties": false
|
|
25
27
|
}
|
|
@@ -32,7 +34,8 @@ export const RUNS_TOOLS = [
|
|
|
32
34
|
"properties": {
|
|
33
35
|
"run_id": {
|
|
34
36
|
"type": "string"
|
|
35
|
-
}
|
|
37
|
+
},
|
|
38
|
+
"branch": BRANCH_PARAM
|
|
36
39
|
},
|
|
37
40
|
"required": [
|
|
38
41
|
"run_id"
|
|
@@ -135,7 +138,8 @@ export const RUNS_TOOLS = [
|
|
|
135
138
|
],
|
|
136
139
|
"additionalProperties": false
|
|
137
140
|
}
|
|
138
|
-
}
|
|
141
|
+
},
|
|
142
|
+
"branch": BRANCH_PARAM
|
|
139
143
|
},
|
|
140
144
|
"required": [
|
|
141
145
|
"title"
|
|
@@ -240,7 +244,8 @@ export const RUNS_TOOLS = [
|
|
|
240
244
|
],
|
|
241
245
|
"additionalProperties": false
|
|
242
246
|
}
|
|
243
|
-
}
|
|
247
|
+
},
|
|
248
|
+
"branch": BRANCH_PARAM
|
|
244
249
|
},
|
|
245
250
|
"required": [
|
|
246
251
|
"run_id"
|
|
@@ -256,7 +261,8 @@ export const RUNS_TOOLS = [
|
|
|
256
261
|
"properties": {
|
|
257
262
|
"run_id": {
|
|
258
263
|
"type": "string"
|
|
259
|
-
}
|
|
264
|
+
},
|
|
265
|
+
"branch": BRANCH_PARAM
|
|
260
266
|
},
|
|
261
267
|
"required": [
|
|
262
268
|
"run_id"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BRANCH_PARAM } from './branches.js';
|
|
1
2
|
export const SUITES_TOOLS = [
|
|
2
3
|
{
|
|
3
4
|
"name": "suites_list",
|
|
@@ -29,7 +30,8 @@ export const SUITES_TOOLS = [
|
|
|
29
30
|
},
|
|
30
31
|
"search_text": {
|
|
31
32
|
"type": "string"
|
|
32
|
-
}
|
|
33
|
+
},
|
|
34
|
+
"branch": BRANCH_PARAM
|
|
33
35
|
},
|
|
34
36
|
"additionalProperties": false
|
|
35
37
|
}
|
|
@@ -42,7 +44,8 @@ export const SUITES_TOOLS = [
|
|
|
42
44
|
"properties": {
|
|
43
45
|
"suite_id": {
|
|
44
46
|
"type": "string"
|
|
45
|
-
}
|
|
47
|
+
},
|
|
48
|
+
"branch": BRANCH_PARAM
|
|
46
49
|
},
|
|
47
50
|
"required": [
|
|
48
51
|
"suite_id"
|
|
@@ -120,7 +123,8 @@ export const SUITES_TOOLS = [
|
|
|
120
123
|
],
|
|
121
124
|
"additionalProperties": false
|
|
122
125
|
}
|
|
123
|
-
}
|
|
126
|
+
},
|
|
127
|
+
"branch": BRANCH_PARAM
|
|
124
128
|
},
|
|
125
129
|
"required": [
|
|
126
130
|
"title"
|
|
@@ -201,7 +205,8 @@ export const SUITES_TOOLS = [
|
|
|
201
205
|
],
|
|
202
206
|
"additionalProperties": false
|
|
203
207
|
}
|
|
204
|
-
}
|
|
208
|
+
},
|
|
209
|
+
"branch": BRANCH_PARAM
|
|
205
210
|
},
|
|
206
211
|
"required": [
|
|
207
212
|
"suite_id"
|
|
@@ -217,7 +222,8 @@ export const SUITES_TOOLS = [
|
|
|
217
222
|
"properties": {
|
|
218
223
|
"suite_id": {
|
|
219
224
|
"type": "string"
|
|
220
|
-
}
|
|
225
|
+
},
|
|
226
|
+
"branch": BRANCH_PARAM
|
|
221
227
|
},
|
|
222
228
|
"required": [
|
|
223
229
|
"suite_id"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TESTS_TQL_INPUT_DESCRIPTION, TESTS_TQL_REFERENCE } from './tql-reference.js';
|
|
2
|
+
import { BRANCH_PARAM } from './branches.js';
|
|
2
3
|
|
|
3
4
|
export const TESTS_TOOLS = [
|
|
4
5
|
{
|
|
@@ -19,7 +20,8 @@ export const TESTS_TOOLS = [
|
|
|
19
20
|
"tql": {
|
|
20
21
|
"type": "string",
|
|
21
22
|
"description": TESTS_TQL_INPUT_DESCRIPTION
|
|
22
|
-
}
|
|
23
|
+
},
|
|
24
|
+
"branch": BRANCH_PARAM
|
|
23
25
|
},
|
|
24
26
|
"additionalProperties": false
|
|
25
27
|
}
|
|
@@ -32,7 +34,8 @@ export const TESTS_TOOLS = [
|
|
|
32
34
|
"properties": {
|
|
33
35
|
"test_id": {
|
|
34
36
|
"type": "string"
|
|
35
|
-
}
|
|
37
|
+
},
|
|
38
|
+
"branch": BRANCH_PARAM
|
|
36
39
|
},
|
|
37
40
|
"required": [
|
|
38
41
|
"test_id"
|
|
@@ -116,7 +119,8 @@ export const TESTS_TOOLS = [
|
|
|
116
119
|
],
|
|
117
120
|
"additionalProperties": false
|
|
118
121
|
}
|
|
119
|
-
}
|
|
122
|
+
},
|
|
123
|
+
"branch": BRANCH_PARAM
|
|
120
124
|
},
|
|
121
125
|
"required": [
|
|
122
126
|
"title",
|
|
@@ -207,7 +211,8 @@ export const TESTS_TOOLS = [
|
|
|
207
211
|
],
|
|
208
212
|
"additionalProperties": false
|
|
209
213
|
}
|
|
210
|
-
}
|
|
214
|
+
},
|
|
215
|
+
"branch": BRANCH_PARAM
|
|
211
216
|
},
|
|
212
217
|
"required": [
|
|
213
218
|
"test_id"
|
|
@@ -223,7 +228,8 @@ export const TESTS_TOOLS = [
|
|
|
223
228
|
"properties": {
|
|
224
229
|
"test_id": {
|
|
225
230
|
"type": "string"
|
|
226
|
-
}
|
|
231
|
+
},
|
|
232
|
+
"branch": BRANCH_PARAM
|
|
227
233
|
},
|
|
228
234
|
"required": [
|
|
229
235
|
"test_id"
|
|
@@ -19,17 +19,27 @@ export const handlerMethods = {
|
|
|
19
19
|
})
|
|
20
20
|
);
|
|
21
21
|
};
|
|
22
|
-
handlers[`${toolPrefix}_get`] = async (args = {}) =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
handlers[`${toolPrefix}_get`] = async (args = {}) => {
|
|
23
|
+
const id = this.pickRequiredArg(args, idArg);
|
|
24
|
+
return this.asText(
|
|
25
|
+
await this.apiClient.get(resource, id, this.pickQueryArgs(spec, args))
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
handlers[`${toolPrefix}_create`] = async (args = {}) => {
|
|
29
|
+
const { query, payloadArgs } = this.splitQueryArgs(spec, args);
|
|
30
|
+
return this.asText(await this.executeCreate(spec, payloadArgs, query));
|
|
31
|
+
};
|
|
26
32
|
handlers[`${toolPrefix}_update`] = async (args = {}) => {
|
|
27
33
|
const id = this.pickRequiredArg(args, idArg);
|
|
28
|
-
const payloadArgs = this.
|
|
29
|
-
return this.asText(await this.executeUpdate(spec, id, payloadArgs));
|
|
34
|
+
const { query, payloadArgs } = this.splitQueryArgs(spec, this.omitArgs(args, [idArg]));
|
|
35
|
+
return this.asText(await this.executeUpdate(spec, id, payloadArgs, query));
|
|
36
|
+
};
|
|
37
|
+
handlers[`${toolPrefix}_delete`] = async (args = {}) => {
|
|
38
|
+
const id = this.pickRequiredArg(args, idArg);
|
|
39
|
+
return this.asText(
|
|
40
|
+
await this.apiClient.delete(resource, id, this.pickQueryArgs(spec, args))
|
|
41
|
+
);
|
|
30
42
|
};
|
|
31
|
-
handlers[`${toolPrefix}_delete`] = async (args = {}) =>
|
|
32
|
-
this.asText(await this.apiClient.delete(resource, this.pickRequiredArg(args, idArg)));
|
|
33
43
|
}
|
|
34
44
|
},
|
|
35
45
|
|
|
@@ -139,34 +149,58 @@ export const handlerMethods = {
|
|
|
139
149
|
return value;
|
|
140
150
|
},
|
|
141
151
|
|
|
142
|
-
|
|
152
|
+
omitArgs(args = {}, keys) {
|
|
143
153
|
const payload = { ...args };
|
|
144
|
-
|
|
154
|
+
for (const key of keys) {
|
|
155
|
+
delete payload[key];
|
|
156
|
+
}
|
|
145
157
|
return payload;
|
|
146
158
|
},
|
|
147
159
|
|
|
148
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Extract the entity's query args (spec.queryArgs, e.g. branch) from tool args
|
|
162
|
+
* without knowing their names in the handlers.
|
|
163
|
+
*/
|
|
164
|
+
splitQueryArgs(spec, args = {}) {
|
|
165
|
+
const queryArgs = spec.queryArgs || [];
|
|
166
|
+
const query = {};
|
|
167
|
+
const payloadArgs = { ...args };
|
|
168
|
+
for (const key of queryArgs) {
|
|
169
|
+
if (args[key] !== undefined) {
|
|
170
|
+
query[key] = args[key];
|
|
171
|
+
delete payloadArgs[key];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { query, payloadArgs };
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
pickQueryArgs(spec, args = {}) {
|
|
178
|
+
const { query } = this.splitQueryArgs(spec, args);
|
|
179
|
+
return query;
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
executeCreate(spec, args = {}, query = {}) {
|
|
149
183
|
if (spec.createMode === 'run') {
|
|
150
|
-
return this.createRunWithFallback(args);
|
|
184
|
+
return this.createRunWithFallback(args, query);
|
|
151
185
|
}
|
|
152
186
|
if (spec.createMode === 'requirement') {
|
|
153
|
-
return this.createRequirement(args);
|
|
187
|
+
return this.createRequirement(args, query);
|
|
154
188
|
}
|
|
155
189
|
const payload = this[spec.payloadBuilder](args);
|
|
156
|
-
return this.createWrapped(spec.resource, spec.wrapperKey, payload);
|
|
190
|
+
return this.createWrapped(spec.resource, spec.wrapperKey, payload, query);
|
|
157
191
|
},
|
|
158
192
|
|
|
159
|
-
executeUpdate(spec, id, args = {}) {
|
|
193
|
+
executeUpdate(spec, id, args = {}, query = {}) {
|
|
160
194
|
if (spec.updateMode === 'run') {
|
|
161
|
-
return this.updateRunWithFallback(id, args);
|
|
195
|
+
return this.updateRunWithFallback(id, args, query);
|
|
162
196
|
}
|
|
163
197
|
if (spec.updateMode === 'requirement') {
|
|
164
|
-
return this.updateRequirement(id, args);
|
|
198
|
+
return this.updateRequirement(id, args, query);
|
|
165
199
|
}
|
|
166
200
|
const payload = this[spec.payloadBuilder](args);
|
|
167
201
|
if (spec.updateMethod === 'patch') {
|
|
168
|
-
return this.patchWrapped(spec.resource, id, spec.wrapperKey, payload);
|
|
202
|
+
return this.patchWrapped(spec.resource, id, spec.wrapperKey, payload, query);
|
|
169
203
|
}
|
|
170
|
-
return this.updateWrapped(spec.resource, id, spec.wrapperKey, payload);
|
|
204
|
+
return this.updateWrapped(spec.resource, id, spec.wrapperKey, payload, query);
|
|
171
205
|
},
|
|
172
206
|
};
|
|
@@ -6,6 +6,7 @@ export const listingMethods = {
|
|
|
6
6
|
count,
|
|
7
7
|
group_by: groupBy,
|
|
8
8
|
slim,
|
|
9
|
+
branch,
|
|
9
10
|
} = {}) {
|
|
10
11
|
return this.apiClient.list('tests', {
|
|
11
12
|
page,
|
|
@@ -14,10 +15,11 @@ export const listingMethods = {
|
|
|
14
15
|
count,
|
|
15
16
|
group_by: groupBy,
|
|
16
17
|
slim,
|
|
18
|
+
branch,
|
|
17
19
|
});
|
|
18
20
|
},
|
|
19
21
|
|
|
20
|
-
listSuites({ page, per_page: perPage, file_type: fileType, tag, labels, search_text: searchText, count, group_by: groupBy, slim } = {}) {
|
|
22
|
+
listSuites({ page, per_page: perPage, file_type: fileType, tag, labels, search_text: searchText, count, group_by: groupBy, slim, branch } = {}) {
|
|
21
23
|
return this.apiClient.list('suites', {
|
|
22
24
|
page,
|
|
23
25
|
per_page: perPage,
|
|
@@ -28,6 +30,7 @@ export const listingMethods = {
|
|
|
28
30
|
count,
|
|
29
31
|
group_by: groupBy,
|
|
30
32
|
slim,
|
|
33
|
+
branch,
|
|
31
34
|
});
|
|
32
35
|
},
|
|
33
36
|
|
|
@@ -38,6 +41,7 @@ export const listingMethods = {
|
|
|
38
41
|
count,
|
|
39
42
|
group_by: groupBy,
|
|
40
43
|
slim,
|
|
44
|
+
branch,
|
|
41
45
|
} = {}) {
|
|
42
46
|
return this.apiClient.list('runs', {
|
|
43
47
|
page,
|
|
@@ -46,6 +50,7 @@ export const listingMethods = {
|
|
|
46
50
|
count,
|
|
47
51
|
group_by: groupBy,
|
|
48
52
|
slim,
|
|
53
|
+
branch,
|
|
49
54
|
});
|
|
50
55
|
},
|
|
51
56
|
|
|
@@ -139,6 +144,26 @@ export const listingMethods = {
|
|
|
139
144
|
return this.apiClient.list('tags', { count, group_by: groupBy, slim });
|
|
140
145
|
},
|
|
141
146
|
|
|
147
|
+
listBranches({
|
|
148
|
+
page,
|
|
149
|
+
per_page: perPage,
|
|
150
|
+
filter_state: filterState,
|
|
151
|
+
filter_title: filterTitle,
|
|
152
|
+
count,
|
|
153
|
+
group_by: groupBy,
|
|
154
|
+
slim,
|
|
155
|
+
} = {}) {
|
|
156
|
+
return this.apiClient.list('branches', {
|
|
157
|
+
page,
|
|
158
|
+
per_page: perPage,
|
|
159
|
+
'filter[state]': filterState,
|
|
160
|
+
'filter[title]': filterTitle,
|
|
161
|
+
count,
|
|
162
|
+
group_by: groupBy,
|
|
163
|
+
slim,
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
|
|
142
167
|
getTagByTitle(tagId) {
|
|
143
168
|
return this.apiClient.get('tags', tagId);
|
|
144
169
|
},
|
|
@@ -2,57 +2,58 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
export const payloadMethods = {
|
|
5
|
-
async createWrapped(resource, wrapperKey, payload) {
|
|
5
|
+
async createWrapped(resource, wrapperKey, payload, query = {}) {
|
|
6
6
|
const wrappedBody = { [wrapperKey]: payload };
|
|
7
7
|
try {
|
|
8
|
-
return await this.apiClient.create(resource, payload);
|
|
8
|
+
return await this.apiClient.create(resource, payload, query);
|
|
9
9
|
} catch (error) {
|
|
10
10
|
if (!this.shouldRetryWrappedBody(error, wrapperKey)) {
|
|
11
11
|
throw error;
|
|
12
12
|
}
|
|
13
|
-
return this.apiClient.create(resource, wrappedBody);
|
|
13
|
+
return this.apiClient.create(resource, wrappedBody, query);
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
|
|
17
|
-
async updateWrapped(resource, id, wrapperKey, payload) {
|
|
17
|
+
async updateWrapped(resource, id, wrapperKey, payload, query = {}) {
|
|
18
18
|
const wrappedBody = { [wrapperKey]: payload };
|
|
19
19
|
try {
|
|
20
|
-
return await this.apiClient.update(resource, id, payload);
|
|
20
|
+
return await this.apiClient.update(resource, id, payload, query);
|
|
21
21
|
} catch (error) {
|
|
22
22
|
if (!this.shouldRetryWrappedBody(error, wrapperKey)) {
|
|
23
23
|
throw error;
|
|
24
24
|
}
|
|
25
|
-
return this.apiClient.update(resource, id, wrappedBody);
|
|
25
|
+
return this.apiClient.update(resource, id, wrappedBody, query);
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
|
|
29
|
-
async patchWrapped(resource, id, wrapperKey, payload) {
|
|
29
|
+
async patchWrapped(resource, id, wrapperKey, payload, query = {}) {
|
|
30
30
|
const wrappedBody = { [wrapperKey]: payload };
|
|
31
31
|
try {
|
|
32
|
-
return await this.apiClient.patch(resource, id, payload);
|
|
32
|
+
return await this.apiClient.patch(resource, id, payload, query);
|
|
33
33
|
} catch (error) {
|
|
34
34
|
if (!this.shouldRetryWrappedBody(error, wrapperKey)) {
|
|
35
35
|
throw error;
|
|
36
36
|
}
|
|
37
|
-
return this.apiClient.patch(resource, id, wrappedBody);
|
|
37
|
+
return this.apiClient.patch(resource, id, wrappedBody, query);
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
|
|
41
|
-
async createRequirement(args = {}) {
|
|
41
|
+
async createRequirement(args = {}, query = {}) {
|
|
42
42
|
const { files, ...payloadArgs } = args;
|
|
43
43
|
const payload = this.buildRequirementPayload(payloadArgs);
|
|
44
44
|
|
|
45
45
|
if (this.hasFiles(files)) {
|
|
46
46
|
return this.apiClient.createMultipart(
|
|
47
47
|
'requirements',
|
|
48
|
-
await this.buildRequirementFormData(payload, files)
|
|
48
|
+
await this.buildRequirementFormData(payload, files),
|
|
49
|
+
query
|
|
49
50
|
);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
return this.createWrapped('requirements', 'requirement', payload);
|
|
53
|
+
return this.createWrapped('requirements', 'requirement', payload, query);
|
|
53
54
|
},
|
|
54
55
|
|
|
55
|
-
async updateRequirement(requirementId, args = {}) {
|
|
56
|
+
async updateRequirement(requirementId, args = {}, query = {}) {
|
|
56
57
|
const { files, ...payloadArgs } = args;
|
|
57
58
|
const payload = this.buildRequirementPayload(payloadArgs);
|
|
58
59
|
|
|
@@ -60,11 +61,12 @@ export const payloadMethods = {
|
|
|
60
61
|
return this.apiClient.patchMultipart(
|
|
61
62
|
'requirements',
|
|
62
63
|
requirementId,
|
|
63
|
-
await this.buildRequirementFormData(payload, files)
|
|
64
|
+
await this.buildRequirementFormData(payload, files),
|
|
65
|
+
query
|
|
64
66
|
);
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
return this.patchWrapped('requirements', requirementId, 'requirement', payload);
|
|
69
|
+
return this.patchWrapped('requirements', requirementId, 'requirement', payload, query);
|
|
68
70
|
},
|
|
69
71
|
|
|
70
72
|
hasFiles(files) {
|
|
@@ -198,27 +200,27 @@ export const payloadMethods = {
|
|
|
198
200
|
};
|
|
199
201
|
},
|
|
200
202
|
|
|
201
|
-
async createRunWithFallback(args = {}) {
|
|
203
|
+
async createRunWithFallback(args = {}, query = {}) {
|
|
202
204
|
const payload = this.buildRunCreatePayload(args);
|
|
203
205
|
try {
|
|
204
|
-
return await this.apiClient.create('runs', payload);
|
|
206
|
+
return await this.apiClient.create('runs', payload, query);
|
|
205
207
|
} catch (error) {
|
|
206
208
|
if (!this.shouldRetryWrappedBody(error, 'run')) {
|
|
207
209
|
throw error;
|
|
208
210
|
}
|
|
209
|
-
return this.apiClient.create('runs', { run: payload });
|
|
211
|
+
return this.apiClient.create('runs', { run: payload }, query);
|
|
210
212
|
}
|
|
211
213
|
},
|
|
212
214
|
|
|
213
|
-
async updateRunWithFallback(runId, args = {}) {
|
|
215
|
+
async updateRunWithFallback(runId, args = {}, query = {}) {
|
|
214
216
|
const payload = this.buildRunUpdatePayload(args);
|
|
215
217
|
try {
|
|
216
|
-
return await this.apiClient.update('runs', runId, payload);
|
|
218
|
+
return await this.apiClient.update('runs', runId, payload, query);
|
|
217
219
|
} catch (error) {
|
|
218
220
|
if (!this.shouldRetryWrappedBody(error, 'run')) {
|
|
219
221
|
throw error;
|
|
220
222
|
}
|
|
221
|
-
return this.apiClient.update('runs', runId, { run: payload });
|
|
223
|
+
return this.apiClient.update('runs', runId, { run: payload }, query);
|
|
222
224
|
}
|
|
223
225
|
},
|
|
224
226
|
|
|
@@ -351,4 +353,8 @@ export const payloadMethods = {
|
|
|
351
353
|
confluence_url: confluenceUrl,
|
|
352
354
|
};
|
|
353
355
|
},
|
|
356
|
+
|
|
357
|
+
buildBranchPayload({ title } = {}) {
|
|
358
|
+
return { title };
|
|
359
|
+
},
|
|
354
360
|
};
|
|
@@ -14,6 +14,7 @@ import { ISSUES_TOOLS } from './definitions/issues.js';
|
|
|
14
14
|
import { PLANS_TOOLS } from './definitions/plans.js';
|
|
15
15
|
import { REQUIREMENTS_TOOLS } from './definitions/requirements.js';
|
|
16
16
|
import { ATTACHMENT_TOOLS } from './definitions/attachments.js';
|
|
17
|
+
import { BRANCHES_TOOLS } from './definitions/branches.js';
|
|
17
18
|
import { withListOptions, withCountGroupOptions } from './list-projection.js';
|
|
18
19
|
|
|
19
20
|
export const TOOL_DEFINITIONS = withCountGroupOptions(withListOptions([
|
|
@@ -33,4 +34,5 @@ export const TOOL_DEFINITIONS = withCountGroupOptions(withListOptions([
|
|
|
33
34
|
...ATTACHMENT_TOOLS,
|
|
34
35
|
...PLANS_TOOLS,
|
|
35
36
|
...REQUIREMENTS_TOOLS,
|
|
37
|
+
...BRANCHES_TOOLS,
|
|
36
38
|
]));
|
package/src/mcp/tool-registry.js
CHANGED
|
@@ -17,7 +17,7 @@ function withPagination(payload) {
|
|
|
17
17
|
if (!Array.isArray(payload.data) || !meta || typeof meta !== 'object') {
|
|
18
18
|
return payload;
|
|
19
19
|
}
|
|
20
|
-
const { total, page, per_page: perPage } = meta;
|
|
20
|
+
const { total, page, per_page: perPage, has_more: backendHasMore } = meta;
|
|
21
21
|
if (
|
|
22
22
|
typeof total !== 'number' ||
|
|
23
23
|
typeof page !== 'number' ||
|
|
@@ -26,7 +26,10 @@ function withPagination(payload) {
|
|
|
26
26
|
) {
|
|
27
27
|
return payload;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
const hasMore =
|
|
30
|
+
typeof backendHasMore === 'boolean' ? backendHasMore : page * perPage < total;
|
|
31
|
+
|
|
32
|
+
if (hasMore) {
|
|
30
33
|
return {
|
|
31
34
|
...payload,
|
|
32
35
|
_note: `Showing ${payload.data.length} of ${total} (page ${page}). More available — refine the filter or request the next page.`,
|