@testomatio/mcp 2.2.0-beta.1 → 2.2.0-beta.2
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
CHANGED
|
@@ -98,9 +98,13 @@ export function slimList(payload, { verbose = false, fields, entity } = {}) {
|
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
100
|
* Ask the API to omit heavy list fields only when MCP does not need them for a
|
|
101
|
-
* full or custom projection.
|
|
101
|
+
* full or custom projection. When `count` is requested the response is meta only,
|
|
102
|
+
* so `slim` is irrelevant and must not be sent alongside `count`.
|
|
102
103
|
*/
|
|
103
|
-
export function backendSlimQuery({ verbose = false, fields } = {}) {
|
|
104
|
+
export function backendSlimQuery({ verbose = false, fields, count = false } = {}) {
|
|
105
|
+
if (count) {
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
104
108
|
return !verbose && !(Array.isArray(fields) && fields.length) ? { slim: true } : {};
|
|
105
109
|
}
|
|
106
110
|
|
|
@@ -146,3 +150,47 @@ export function withListOptions(tools, { extraNames = [] } = {}) {
|
|
|
146
150
|
};
|
|
147
151
|
});
|
|
148
152
|
}
|
|
153
|
+
|
|
154
|
+
// Scoped list tools (*_issues_list, *_attachments_list) are filtered to one entity,
|
|
155
|
+
// so count/group_by aggregation does not apply there.
|
|
156
|
+
const SCOPED_LIST_INFIXES = ['_issues_', '_attachments_'];
|
|
157
|
+
|
|
158
|
+
function isPrimaryListToolName(name) {
|
|
159
|
+
return (
|
|
160
|
+
typeof name === 'string' &&
|
|
161
|
+
name.endsWith('_list') &&
|
|
162
|
+
!SCOPED_LIST_INFIXES.some((infix) => name.includes(infix))
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const COUNT_PROPERTY = {
|
|
167
|
+
type: 'boolean',
|
|
168
|
+
description:
|
|
169
|
+
'Return only metadata with total counts instead of the entity list. Pair with group_by for an aggregated breakdown.',
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const GROUP_BY_PROPERTY = {
|
|
173
|
+
type: 'string',
|
|
174
|
+
description:
|
|
175
|
+
'Aggregate counts by this field, e.g. status, state, priority, created_by (use with count=true). The backend validates supported fields per resource. For created_by, counts are keyed by user email, not ID.',
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Inject `count` and `group_by` into every primary list tool. `group_by` is a
|
|
180
|
+
* free-form string — the backend is the source of truth for which fields each
|
|
181
|
+
* resource accepts. Scoped list tools (*_issues_list, *_attachments_list) are skipped.
|
|
182
|
+
*
|
|
183
|
+
* @param {Array} tools
|
|
184
|
+
*/
|
|
185
|
+
export function withCountGroupOptions(tools) {
|
|
186
|
+
return tools.map((tool) => {
|
|
187
|
+
if (!tool || !isPrimaryListToolName(tool.name)) return tool;
|
|
188
|
+
const inputSchema = tool.inputSchema || { type: 'object', properties: {} };
|
|
189
|
+
const properties = {
|
|
190
|
+
...(inputSchema.properties || {}),
|
|
191
|
+
count: COUNT_PROPERTY,
|
|
192
|
+
group_by: GROUP_BY_PROPERTY,
|
|
193
|
+
};
|
|
194
|
+
return { ...tool, inputSchema: { ...inputSchema, properties } };
|
|
195
|
+
});
|
|
196
|
+
}
|
|
@@ -10,7 +10,7 @@ export const handlerMethods = {
|
|
|
10
10
|
|
|
11
11
|
handlers[`${toolPrefix}_list`] = async (args = {}) => {
|
|
12
12
|
const { verbose, fields, ...listArgs } = args;
|
|
13
|
-
Object.assign(listArgs, backendSlimQuery({ verbose, fields }));
|
|
13
|
+
Object.assign(listArgs, backendSlimQuery({ verbose, fields, count: listArgs.count }));
|
|
14
14
|
return this.asText(
|
|
15
15
|
slimList(await this[listMethod](listArgs), {
|
|
16
16
|
verbose,
|
|
@@ -102,15 +102,16 @@ export const handlerMethods = {
|
|
|
102
102
|
registerGlobalHandlers(handlers) {
|
|
103
103
|
handlers.project_info = async () => this.asText(await this.apiClient.get('info'));
|
|
104
104
|
|
|
105
|
-
handlers.tags_list = async (args = {}) =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
);
|
|
105
|
+
handlers.tags_list = async (args = {}) => {
|
|
106
|
+
const { verbose, fields, ...listArgs } = args;
|
|
107
|
+
Object.assign(listArgs, backendSlimQuery({ verbose, fields, count: listArgs.count }));
|
|
108
|
+
return this.asText(slimList(await this.listTags(listArgs), { ...args, entity: 'tags' }));
|
|
109
|
+
};
|
|
109
110
|
handlers.tags_get = async ({ tag_id: tagId }) => this.asText(await this.getTagByTitle(tagId));
|
|
110
111
|
|
|
111
112
|
handlers.milestones_list = async (args = {}) => {
|
|
112
113
|
const { verbose, fields, ...listArgs } = args;
|
|
113
|
-
Object.assign(listArgs, backendSlimQuery({ verbose, fields }));
|
|
114
|
+
Object.assign(listArgs, backendSlimQuery({ verbose, fields, count: listArgs.count }));
|
|
114
115
|
return this.asText(
|
|
115
116
|
slimList(await this.listMilestones(listArgs), { verbose, fields, entity: 'milestones' })
|
|
116
117
|
);
|
|
@@ -120,7 +121,7 @@ export const handlerMethods = {
|
|
|
120
121
|
|
|
121
122
|
handlers.issues_list = async (args = {}) => {
|
|
122
123
|
const { verbose, fields, ...listArgs } = args;
|
|
123
|
-
Object.assign(listArgs, backendSlimQuery({ verbose, fields }));
|
|
124
|
+
Object.assign(listArgs, backendSlimQuery({ verbose, fields, count: listArgs.count }));
|
|
124
125
|
return this.asText(
|
|
125
126
|
slimList(await this.listIssues(listArgs), { verbose, fields, entity: 'issues' })
|
|
126
127
|
);
|
|
@@ -3,17 +3,21 @@ export const listingMethods = {
|
|
|
3
3
|
page,
|
|
4
4
|
per_page: perPage,
|
|
5
5
|
tql,
|
|
6
|
+
count,
|
|
7
|
+
group_by: groupBy,
|
|
6
8
|
slim,
|
|
7
9
|
} = {}) {
|
|
8
10
|
return this.apiClient.list('tests', {
|
|
9
11
|
page,
|
|
10
12
|
per_page: perPage,
|
|
11
13
|
tql,
|
|
14
|
+
count,
|
|
15
|
+
group_by: groupBy,
|
|
12
16
|
slim,
|
|
13
17
|
});
|
|
14
18
|
},
|
|
15
19
|
|
|
16
|
-
listSuites({ page, per_page: perPage, file_type: fileType, tag, labels, search_text: searchText, slim } = {}) {
|
|
20
|
+
listSuites({ page, per_page: perPage, file_type: fileType, tag, labels, search_text: searchText, count, group_by: groupBy, slim } = {}) {
|
|
17
21
|
return this.apiClient.list('suites', {
|
|
18
22
|
page,
|
|
19
23
|
per_page: perPage,
|
|
@@ -21,6 +25,8 @@ export const listingMethods = {
|
|
|
21
25
|
tag,
|
|
22
26
|
labels,
|
|
23
27
|
search_text: searchText,
|
|
28
|
+
count,
|
|
29
|
+
group_by: groupBy,
|
|
24
30
|
slim,
|
|
25
31
|
});
|
|
26
32
|
},
|
|
@@ -29,12 +35,16 @@ export const listingMethods = {
|
|
|
29
35
|
page,
|
|
30
36
|
per_page: perPage,
|
|
31
37
|
tql,
|
|
38
|
+
count,
|
|
39
|
+
group_by: groupBy,
|
|
32
40
|
slim,
|
|
33
41
|
} = {}) {
|
|
34
42
|
return this.apiClient.list('runs', {
|
|
35
43
|
page,
|
|
36
44
|
per_page: perPage,
|
|
37
45
|
tql,
|
|
46
|
+
count,
|
|
47
|
+
group_by: groupBy,
|
|
38
48
|
slim,
|
|
39
49
|
});
|
|
40
50
|
},
|
|
@@ -58,6 +68,8 @@ export const listingMethods = {
|
|
|
58
68
|
envs,
|
|
59
69
|
rungroups,
|
|
60
70
|
defects,
|
|
71
|
+
count,
|
|
72
|
+
group_by: groupBy,
|
|
61
73
|
slim,
|
|
62
74
|
} = {}) {
|
|
63
75
|
return this.apiClient.list('testruns', {
|
|
@@ -79,27 +91,29 @@ export const listingMethods = {
|
|
|
79
91
|
envs: Array.isArray(envs) ? envs.join(',') : envs,
|
|
80
92
|
rungroups: Array.isArray(rungroups) ? rungroups.join(',') : rungroups,
|
|
81
93
|
defects,
|
|
94
|
+
count,
|
|
95
|
+
group_by: groupBy,
|
|
82
96
|
slim,
|
|
83
97
|
});
|
|
84
98
|
},
|
|
85
99
|
|
|
86
|
-
listRungroups({ page, per_page: perPage, slim } = {}) {
|
|
87
|
-
return this.apiClient.list('rungroups', { page, per_page: perPage, slim });
|
|
100
|
+
listRungroups({ page, per_page: perPage, count, group_by: groupBy, slim } = {}) {
|
|
101
|
+
return this.apiClient.list('rungroups', { page, per_page: perPage, count, group_by: groupBy, slim });
|
|
88
102
|
},
|
|
89
103
|
|
|
90
|
-
listSteps({ page, per_page: perPage, slim } = {}) {
|
|
91
|
-
return this.apiClient.list('steps', { page, per_page: perPage, slim });
|
|
104
|
+
listSteps({ page, per_page: perPage, count, group_by: groupBy, slim } = {}) {
|
|
105
|
+
return this.apiClient.list('steps', { page, per_page: perPage, count, group_by: groupBy, slim });
|
|
92
106
|
},
|
|
93
107
|
|
|
94
|
-
listSnippets({ page, per_page: perPage, slim } = {}) {
|
|
95
|
-
return this.apiClient.list('snippets', { page, per_page: perPage, slim });
|
|
108
|
+
listSnippets({ page, per_page: perPage, count, group_by: groupBy, slim } = {}) {
|
|
109
|
+
return this.apiClient.list('snippets', { page, per_page: perPage, count, group_by: groupBy, slim });
|
|
96
110
|
},
|
|
97
111
|
|
|
98
|
-
listLabels({ page, per_page: perPage, slim } = {}) {
|
|
99
|
-
return this.apiClient.list('labels', { page, per_page: perPage, slim });
|
|
112
|
+
listLabels({ page, per_page: perPage, count, group_by: groupBy, slim } = {}) {
|
|
113
|
+
return this.apiClient.list('labels', { page, per_page: perPage, count, group_by: groupBy, slim });
|
|
100
114
|
},
|
|
101
115
|
|
|
102
|
-
listPlans({ page, per_page: perPage, kind, hidden, labels, search_text: searchText, slim } = {}) {
|
|
116
|
+
listPlans({ page, per_page: perPage, kind, hidden, labels, search_text: searchText, count, group_by: groupBy, slim } = {}) {
|
|
103
117
|
return this.apiClient.list('plans', {
|
|
104
118
|
page,
|
|
105
119
|
per_page: perPage,
|
|
@@ -107,20 +121,22 @@ export const listingMethods = {
|
|
|
107
121
|
hidden,
|
|
108
122
|
'labels[]': labels,
|
|
109
123
|
search_text: searchText,
|
|
124
|
+
count,
|
|
125
|
+
group_by: groupBy,
|
|
110
126
|
slim,
|
|
111
127
|
});
|
|
112
128
|
},
|
|
113
129
|
|
|
114
|
-
listRequirements({ page, per_page: perPage, source, scope, slim } = {}) {
|
|
115
|
-
return this.apiClient.list('requirements', { page, per_page: perPage, source, scope, slim });
|
|
130
|
+
listRequirements({ page, per_page: perPage, source, scope, count, group_by: groupBy, slim } = {}) {
|
|
131
|
+
return this.apiClient.list('requirements', { page, per_page: perPage, source, scope, count, group_by: groupBy, slim });
|
|
116
132
|
},
|
|
117
133
|
|
|
118
|
-
listMilestones({ page, per_page: perPage, type, status, slim } = {}) {
|
|
119
|
-
return this.apiClient.list('milestones', { page, per_page: perPage, type, status, slim });
|
|
134
|
+
listMilestones({ page, per_page: perPage, type, status, count, group_by: groupBy, slim } = {}) {
|
|
135
|
+
return this.apiClient.list('milestones', { page, per_page: perPage, type, status, count, group_by: groupBy, slim });
|
|
120
136
|
},
|
|
121
137
|
|
|
122
|
-
listTags({ slim } = {}) {
|
|
123
|
-
return this.apiClient.list('tags', { slim });
|
|
138
|
+
listTags({ count, group_by: groupBy, slim } = {}) {
|
|
139
|
+
return this.apiClient.list('tags', { count, group_by: groupBy, slim });
|
|
124
140
|
},
|
|
125
141
|
|
|
126
142
|
getTagByTitle(tagId) {
|
|
@@ -14,9 +14,9 @@ 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 { withListOptions } from './list-projection.js';
|
|
17
|
+
import { withListOptions, withCountGroupOptions } from './list-projection.js';
|
|
18
18
|
|
|
19
|
-
export const TOOL_DEFINITIONS = withListOptions([
|
|
19
|
+
export const TOOL_DEFINITIONS = withCountGroupOptions(withListOptions([
|
|
20
20
|
...SYSTEM_TOOLS,
|
|
21
21
|
...PROJECT_TOOLS,
|
|
22
22
|
...TESTS_TOOLS,
|
|
@@ -33,4 +33,4 @@ export const TOOL_DEFINITIONS = withListOptions([
|
|
|
33
33
|
...ATTACHMENT_TOOLS,
|
|
34
34
|
...PLANS_TOOLS,
|
|
35
35
|
...REQUIREMENTS_TOOLS,
|
|
36
|
-
]);
|
|
36
|
+
]));
|