@testomatio/mcp 3.0.0-beta.1 → 3.0.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
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export const SHARES_TOOLS = [
|
|
2
|
+
{
|
|
3
|
+
"name": "tests_share",
|
|
4
|
+
"description": "Share tests into a suite of another project (/api/v2/{project_id}/shares/tests). Select tests by test_ids, labels, or both (combined). The source project stays the single source of truth; shared copies in the target project are read-only until unlinked. Re-sharing into the same target project does not duplicate. Requests are processed asynchronously: status 'queued' means accepted, not completed. Matched tests that are themselves shared copies are skipped and listed in skipped_test_ids. Source and target projects must be of the same type (Classic/BDD).",
|
|
5
|
+
"inputSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"test_ids": {
|
|
9
|
+
"type": "array",
|
|
10
|
+
"items": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
"description": "Test IDs to share. At least one of test_ids/labels is required. Max 1000 tests per request."
|
|
14
|
+
},
|
|
15
|
+
"labels": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": {
|
|
18
|
+
"type": "string"
|
|
19
|
+
},
|
|
20
|
+
"description": "Label slugs or titles; every test carrying any of these labels is shared, in addition to test_ids."
|
|
21
|
+
},
|
|
22
|
+
"target_project_id": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "Project ID (slug) of the destination project. Must be accessible to the current user."
|
|
25
|
+
},
|
|
26
|
+
"target_suite_id": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Suite ID in the target project to place the shared tests into. Must be a file-type suite, not a folder."
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"required": [
|
|
32
|
+
"target_project_id",
|
|
33
|
+
"target_suite_id"
|
|
34
|
+
],
|
|
35
|
+
"additionalProperties": false
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "suites_share",
|
|
40
|
+
"description": "Share suites (with their tests) into one or more other projects (/api/v2/{project_id}/shares/suites). Select suites by suite_ids, labels, or both (combined). File-type suites are linked (read-only copies that stay in sync); folder suites are deep-copied as regular editable copies. Re-sharing a linked suite into a project that already has it does not duplicate. Suites that are themselves shared copies link to their original source. Omit destination_folder_id to share into the root of the target project(s). Requests are processed asynchronously: status 'queued' means accepted, not completed. Source and target projects must be of the same type (Classic/BDD).",
|
|
41
|
+
"inputSchema": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"suite_ids": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"items": {
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
"description": "Suite IDs to share. At least one of suite_ids/labels is required. Max 200 suites per request."
|
|
50
|
+
},
|
|
51
|
+
"labels": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": {
|
|
54
|
+
"type": "string"
|
|
55
|
+
},
|
|
56
|
+
"description": "Label slugs or titles; every suite carrying any of these labels is shared, in addition to suite_ids."
|
|
57
|
+
},
|
|
58
|
+
"target_project_ids": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": {
|
|
61
|
+
"type": "string"
|
|
62
|
+
},
|
|
63
|
+
"description": "Project IDs (slugs) of the destination projects. Must be accessible to the current user."
|
|
64
|
+
},
|
|
65
|
+
"destination_folder_id": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"description": "Folder suite ID in the target project to place the shared suites into. Only allowed when sharing to a single target project."
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"required": [
|
|
71
|
+
"target_project_ids"
|
|
72
|
+
],
|
|
73
|
+
"additionalProperties": false
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "tests_unshare",
|
|
78
|
+
"description": "Remove a test's share, converting the shared copy back into a regular, editable test (/api/v2/{project_id}/shares/tests/{id}). Only the shared copy can be targeted — the original source test is untouched. Must be called against the project that holds the shared copy.",
|
|
79
|
+
"inputSchema": {
|
|
80
|
+
"type": "object",
|
|
81
|
+
"properties": {
|
|
82
|
+
"test_id": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"description": "ID of the shared test copy to unlink."
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"required": [
|
|
88
|
+
"test_id"
|
|
89
|
+
],
|
|
90
|
+
"additionalProperties": false
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"name": "suites_unshare",
|
|
95
|
+
"description": "Remove a suite's share, converting the shared copy back into a regular, editable suite (/api/v2/{project_id}/shares/suites/{id}). Only the shared (linked) copy can be targeted — the original source suite is untouched. Must be called against the project that holds the shared copy.",
|
|
96
|
+
"inputSchema": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"properties": {
|
|
99
|
+
"suite_id": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"description": "ID of the shared suite copy to unlink."
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"required": [
|
|
105
|
+
"suite_id"
|
|
106
|
+
],
|
|
107
|
+
"additionalProperties": false
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const shareMethods = {
|
|
2
|
+
registerShareHandlers(handlers) {
|
|
3
|
+
handlers.tests_share = async (args = {}) => {
|
|
4
|
+
const payload = {
|
|
5
|
+
target_project_id: this.pickRequiredArg(args, 'target_project_id'),
|
|
6
|
+
target_suite_id: this.pickRequiredArg(args, 'target_suite_id'),
|
|
7
|
+
};
|
|
8
|
+
this.pickSelectionArgs(args, ['test_ids', 'labels'], payload);
|
|
9
|
+
return this.asText(await this.apiClient.create('shares/tests', payload));
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
handlers.suites_share = async (args = {}) => {
|
|
13
|
+
const payload = {
|
|
14
|
+
target_project_ids: this.pickRequiredArg(args, 'target_project_ids'),
|
|
15
|
+
};
|
|
16
|
+
this.pickSelectionArgs(args, ['suite_ids', 'labels'], payload);
|
|
17
|
+
if (args.destination_folder_id !== undefined) {
|
|
18
|
+
payload.destination_folder_id = args.destination_folder_id;
|
|
19
|
+
}
|
|
20
|
+
return this.asText(await this.apiClient.create('shares/suites', payload));
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
handlers.tests_unshare = async (args = {}) =>
|
|
24
|
+
this.asText(await this.apiClient.delete('shares/tests', this.pickRequiredArg(args, 'test_id')));
|
|
25
|
+
|
|
26
|
+
handlers.suites_unshare = async (args = {}) =>
|
|
27
|
+
this.asText(
|
|
28
|
+
await this.apiClient.delete('shares/suites', this.pickRequiredArg(args, 'suite_id'))
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Copy the selection args (e.g. test_ids/labels) into the payload, requiring at least
|
|
34
|
+
* one of them — the API rejects a share request without a selection.
|
|
35
|
+
*/
|
|
36
|
+
pickSelectionArgs(args = {}, keys, payload) {
|
|
37
|
+
const provided = keys.filter((key) => {
|
|
38
|
+
const value = args[key];
|
|
39
|
+
if (value === undefined || value === null || value === '') return false;
|
|
40
|
+
if (Array.isArray(value) && value.length === 0) return false;
|
|
41
|
+
return true;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (provided.length === 0) {
|
|
45
|
+
throw new Error(`Provide at least one selection argument: ${keys.join(', ')}.`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const key of provided) {
|
|
49
|
+
payload[key] = args[key];
|
|
50
|
+
}
|
|
51
|
+
return provided;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -2,6 +2,7 @@ import { SYSTEM_TOOLS } from './definitions/system.js';
|
|
|
2
2
|
import { PROJECT_TOOLS } from './definitions/projects.js';
|
|
3
3
|
import { TESTS_TOOLS } from './definitions/tests.js';
|
|
4
4
|
import { SUITES_TOOLS } from './definitions/suites.js';
|
|
5
|
+
import { SHARES_TOOLS } from './definitions/shares.js';
|
|
5
6
|
import { RUNS_TOOLS } from './definitions/runs.js';
|
|
6
7
|
import { TESTRUNS_TOOLS } from './definitions/testruns.js';
|
|
7
8
|
import { RUNGROUPS_TOOLS } from './definitions/rungroups.js';
|
|
@@ -22,6 +23,7 @@ export const TOOL_DEFINITIONS = withCountGroupOptions(withListOptions([
|
|
|
22
23
|
...PROJECT_TOOLS,
|
|
23
24
|
...TESTS_TOOLS,
|
|
24
25
|
...SUITES_TOOLS,
|
|
26
|
+
...SHARES_TOOLS,
|
|
25
27
|
...RUNS_TOOLS,
|
|
26
28
|
...TESTRUNS_TOOLS,
|
|
27
29
|
...RUNGROUPS_TOOLS,
|
package/src/mcp/tool-registry.js
CHANGED
|
@@ -8,6 +8,7 @@ import { attachmentMethods } from './registry/attachments.js';
|
|
|
8
8
|
import { issueMethods } from './registry/issues.js';
|
|
9
9
|
import { listingMethods } from './registry/listings.js';
|
|
10
10
|
import { payloadMethods } from './registry/payloads.js';
|
|
11
|
+
import { shareMethods } from './registry/shares.js';
|
|
11
12
|
|
|
12
13
|
function withPagination(payload) {
|
|
13
14
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
@@ -72,6 +73,7 @@ export class ToolRegistry {
|
|
|
72
73
|
this.registerScopedIssueHandlers(handlers);
|
|
73
74
|
this.registerScopedAttachmentHandlers(handlers);
|
|
74
75
|
this.registerGlobalHandlers(handlers);
|
|
76
|
+
this.registerShareHandlers(handlers);
|
|
75
77
|
for (const registerHandlers of this.handlerRegistrars) {
|
|
76
78
|
registerHandlers.call(this, handlers);
|
|
77
79
|
}
|
|
@@ -116,5 +118,6 @@ Object.assign(
|
|
|
116
118
|
attachmentMethods,
|
|
117
119
|
listingMethods,
|
|
118
120
|
issueMethods,
|
|
119
|
-
payloadMethods
|
|
121
|
+
payloadMethods,
|
|
122
|
+
shareMethods
|
|
120
123
|
);
|