@smartbear/mcp 0.22.0 → 0.24.0
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 +28 -4
- package/dist/bearq/client.js +81 -0
- package/dist/bearq/config/constants.js +6 -0
- package/dist/bearq/tool/tasks/chat-with-qa-lead.js +32 -0
- package/dist/bearq/tool/tasks/expand-application-model.js +34 -0
- package/dist/bearq/tool/tasks/get-task-status.js +32 -0
- package/dist/bearq/tool/tasks/get-task.js +29 -0
- package/dist/bearq/tool/tasks/refine-all-draft-tests.js +27 -0
- package/dist/bearq/tool/tasks/refine-test-cases.js +30 -0
- package/dist/bearq/tool/tasks/refine-tests-in-functional-areas.js +34 -0
- package/dist/bearq/tool/tasks/run-regression-tests.js +27 -0
- package/dist/bearq/tool/tasks/run-test-cases.js +30 -0
- package/dist/bearq/tool/tasks/run-tests-in-functional-areas.js +30 -0
- package/dist/bearq/tool/tasks/stop-task.js +29 -0
- package/dist/bearq/tool/tasks/wait-for-task.js +83 -0
- package/dist/common/register-clients.js +2 -0
- package/dist/package.json.js +1 -1
- package/dist/pactflow/client/base.js +3 -3
- package/dist/qtm4j/client.js +26 -2
- package/dist/qtm4j/config/constants.js +96 -4
- package/dist/qtm4j/config/field-resolution.types.js +2 -1
- package/dist/qtm4j/http/api-client.js +70 -1
- package/dist/qtm4j/resolver/cache/cache.js +1 -1
- package/dist/qtm4j/resolver/resolvers/common-attribute-resolver.js +1 -0
- package/dist/qtm4j/resolver/resolvers/component-resolver.js +2 -0
- package/dist/qtm4j/resolver/resolvers/label-resolver.js +2 -0
- package/dist/qtm4j/schema/automation.schema.js +107 -0
- package/dist/qtm4j/schema/search-test-cycle.schema.js +133 -0
- package/dist/qtm4j/schema/test-cycle.schema.js +39 -0
- package/dist/qtm4j/schema/update-test-cycle.schema.js +54 -0
- package/dist/qtm4j/tool/test-automation/get-automation-history.js +69 -0
- package/dist/qtm4j/tool/test-automation/upload-automation-result.js +143 -0
- package/dist/qtm4j/tool/test-cycle/create-test-cycle.js +80 -0
- package/dist/qtm4j/tool/test-cycle/search-test-cycle.js +136 -0
- package/dist/qtm4j/tool/test-cycle/update-test-cycle.js +161 -0
- package/dist/swagger/client/portal-types.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
</div>
|
|
19
19
|
<br />
|
|
20
20
|
|
|
21
|
-
A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [BugSnag](https://www.bugsnag.com/), [Reflect](https://reflect.run), [Swagger](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/), [Pact Broker](https://docs.pact.io/), [QMetry](https://www.qmetry.com/), [QTM4J](https://www.qmetry.com/qmetry-test-management-for-jira), [Zephyr](https://smartbear.com/test-management/zephyr/) and [Collaborator](https://smartbear.com/product/collaborator/).
|
|
21
|
+
A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [BearQ](https://developer.smartbear.com/smartbear-mcp/docs/bearq-integration), [BugSnag](https://www.bugsnag.com/), [Reflect](https://reflect.run), [Swagger](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/), [Pact Broker](https://docs.pact.io/), [QMetry](https://www.qmetry.com/), [QTM4J](https://www.qmetry.com/qmetry-test-management-for-jira), [Zephyr](https://smartbear.com/test-management/zephyr/) and [Collaborator](https://smartbear.com/product/collaborator/).
|
|
22
22
|
|
|
23
23
|
## What is MCP?
|
|
24
24
|
|
|
@@ -28,6 +28,7 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction)
|
|
|
28
28
|
|
|
29
29
|
See individual guides for suggested prompts and supported tools and resources:
|
|
30
30
|
|
|
31
|
+
- [BearQ](https://developer.smartbear.com/smartbear-mcp/docs/bearq-integration) - AI-powered QA test management and execution capabilities
|
|
31
32
|
- [BugSnag](https://developer.smartbear.com/smartbear-mcp/docs/bugsnag-integration) - Comprehensive error monitoring and debugging capabilities
|
|
32
33
|
- [Reflect](https://developer.smartbear.com/smartbear-mcp/docs/reflect-integration) - Test management and execution capabilities
|
|
33
34
|
- **Swagger**
|
|
@@ -51,7 +52,7 @@ For BugSnag, Swagger, and Zephyr, SmartBear hosts Remote MCP Servers that you ca
|
|
|
51
52
|
|
|
52
53
|
See the [Remote MCP Servers guide](https://developer.smartbear.com/smartbear-mcp/docs/remote-mcp-servers) for per-client setup instructions. You can connect to multiple remote servers at the same time.
|
|
53
54
|
|
|
54
|
-
> **Need Reflect, QMetry, QTM4J, PactFlow, or Collaborator?** These products are only available via the local npm package below, which bundles all products into a single MCP server.
|
|
55
|
+
> **Need BearQ, Reflect, QMetry, QTM4J, PactFlow, or Collaborator?** These products are only available via the local npm package below, which bundles all products into a single MCP server.
|
|
55
56
|
|
|
56
57
|
## Prerequisites
|
|
57
58
|
|
|
@@ -85,6 +86,8 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
|
|
|
85
86
|
"@smartbear/mcp@latest"
|
|
86
87
|
],
|
|
87
88
|
"env": {
|
|
89
|
+
"BEARQ_API_TOKEN": "${input:bearq_api_token}",
|
|
90
|
+
"BEARQ_API_BASE_URL": "${input:bearq_api_base_url}",
|
|
88
91
|
"BUGSNAG_AUTH_TOKEN": "${input:bugsnag_auth_token}",
|
|
89
92
|
"BUGSNAG_PROJECT_API_KEY": "${input:bugsnag_project_api_key}",
|
|
90
93
|
"REFLECT_API_TOKEN": "${input:reflect_api_token}",
|
|
@@ -104,11 +107,24 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
|
|
|
104
107
|
"COLLABORATOR_USERNAME": "${input:collab_username}",
|
|
105
108
|
"COLLABORATOR_LOGIN_TICKET": "${input:collab_login_ticket}",
|
|
106
109
|
"QTM4J_API_KEY": "${input:qtm4j_api_key}",
|
|
107
|
-
"QTM4J_BASE_URL": "${input:qtm4j_base_url}"
|
|
110
|
+
"QTM4J_BASE_URL": "${input:qtm4j_base_url}",
|
|
111
|
+
"QTM4J_AUTOMATION_API_KEY": "${input:qtm4j_automation_api_key}"
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
114
|
},
|
|
111
115
|
"inputs": [
|
|
116
|
+
{
|
|
117
|
+
"id": "bearq_api_token",
|
|
118
|
+
"type": "promptString",
|
|
119
|
+
"description": "BearQ workspace API token - leave blank to disable BearQ tools",
|
|
120
|
+
"password": true
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "bearq_api_base_url",
|
|
124
|
+
"type": "promptString",
|
|
125
|
+
"description": "BearQ API base URL - leave blank to use the default (https://api.bearq.smartbear.com)",
|
|
126
|
+
"password": false
|
|
127
|
+
},
|
|
112
128
|
{
|
|
113
129
|
"id": "bugsnag_auth_token",
|
|
114
130
|
"type": "promptString",
|
|
@@ -228,6 +244,12 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
|
|
|
228
244
|
"type": "promptString",
|
|
229
245
|
"description": "US region (default): https://qtmcloud.qmetry.com. Australia region: https://syd-qtmcloud.qmetry.com.",
|
|
230
246
|
"password": false
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"id": "qtm4j_automation_api_key",
|
|
250
|
+
"type": "promptString",
|
|
251
|
+
"description": "QTM4J Automation API Key - required for automation tools, leave blank to disable them",
|
|
252
|
+
"password": true
|
|
231
253
|
}
|
|
232
254
|
]
|
|
233
255
|
}
|
|
@@ -248,6 +270,7 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
|
|
|
248
270
|
"@smartbear/mcp@latest"
|
|
249
271
|
],
|
|
250
272
|
"env": {
|
|
273
|
+
"BEARQ_API_TOKEN": "your_bearq_api_token",
|
|
251
274
|
"BUGSNAG_AUTH_TOKEN": "your_personal_auth_token",
|
|
252
275
|
"BUGSNAG_PROJECT_API_KEY": "your_project_api_key",
|
|
253
276
|
"REFLECT_API_TOKEN": "your_reflect_token",
|
|
@@ -267,7 +290,8 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
|
|
|
267
290
|
"COLLABORATOR_USERNAME": "your collab user name",
|
|
268
291
|
"COLLABORATOR_LOGIN_TICKET": "your collab login ticket",
|
|
269
292
|
"QTM4J_API_KEY": "your_qtm4j_key",
|
|
270
|
-
"QTM4J_BASE_URL": "https://qtmcloud.qmetry.com"
|
|
293
|
+
"QTM4J_BASE_URL": "https://qtmcloud.qmetry.com",
|
|
294
|
+
"QTM4J_AUTOMATION_API_KEY": "your_qtm4j_automation_api_key"
|
|
271
295
|
}
|
|
272
296
|
}
|
|
273
297
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
|
|
3
|
+
import { getRequestHeader } from "../common/request-context.js";
|
|
4
|
+
import { ToolError } from "../common/tools.js";
|
|
5
|
+
import { DEFAULT_API_BASE_URL, AUTHORIZATION_HEADER } from "./config/constants.js";
|
|
6
|
+
import { ChatWithQaLead } from "./tool/tasks/chat-with-qa-lead.js";
|
|
7
|
+
import { ExpandApplicationModel } from "./tool/tasks/expand-application-model.js";
|
|
8
|
+
import { GetTask } from "./tool/tasks/get-task.js";
|
|
9
|
+
import { GetTaskStatus } from "./tool/tasks/get-task-status.js";
|
|
10
|
+
import { RefineAllDraftTests } from "./tool/tasks/refine-all-draft-tests.js";
|
|
11
|
+
import { RefineTestCases } from "./tool/tasks/refine-test-cases.js";
|
|
12
|
+
import { RefineTestsInFunctionalAreas } from "./tool/tasks/refine-tests-in-functional-areas.js";
|
|
13
|
+
import { RunRegressionTests } from "./tool/tasks/run-regression-tests.js";
|
|
14
|
+
import { RunTestCases } from "./tool/tasks/run-test-cases.js";
|
|
15
|
+
import { RunTestsInFunctionalAreas } from "./tool/tasks/run-tests-in-functional-areas.js";
|
|
16
|
+
import { StopTask } from "./tool/tasks/stop-task.js";
|
|
17
|
+
import { WaitForTask } from "./tool/tasks/wait-for-task.js";
|
|
18
|
+
const ConfigurationSchema = z.object({
|
|
19
|
+
api_token: z.string().describe("BearQ workspace API token (Bearer)."),
|
|
20
|
+
api_base_url: z.string().optional().describe(
|
|
21
|
+
"Override the BearQ public API base URL. Defaults to https://api.bearq.smartbear.com"
|
|
22
|
+
)
|
|
23
|
+
});
|
|
24
|
+
class BearQClient {
|
|
25
|
+
_apiToken;
|
|
26
|
+
_baseUrl = DEFAULT_API_BASE_URL;
|
|
27
|
+
name = "BearQ";
|
|
28
|
+
capabilityPrefix = "bearq";
|
|
29
|
+
configPrefix = "BearQ";
|
|
30
|
+
config = ConfigurationSchema;
|
|
31
|
+
async configure(_server, config) {
|
|
32
|
+
this._apiToken = config.api_token;
|
|
33
|
+
if (config.api_base_url) this._baseUrl = config.api_base_url;
|
|
34
|
+
}
|
|
35
|
+
getAuthToken() {
|
|
36
|
+
const contextHeader = getRequestHeader(AUTHORIZATION_HEADER);
|
|
37
|
+
if (contextHeader) {
|
|
38
|
+
let token = Array.isArray(contextHeader) ? contextHeader[0] : contextHeader;
|
|
39
|
+
if (token.startsWith("Bearer ")) token = token.substring(7);
|
|
40
|
+
return token;
|
|
41
|
+
}
|
|
42
|
+
return this._apiToken ?? null;
|
|
43
|
+
}
|
|
44
|
+
isConfigured() {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
getBaseUrl() {
|
|
48
|
+
return this._baseUrl;
|
|
49
|
+
}
|
|
50
|
+
getHeaders() {
|
|
51
|
+
const token = this.getAuthToken();
|
|
52
|
+
if (!token) throw new ToolError("BearQ API token not found");
|
|
53
|
+
return {
|
|
54
|
+
Authorization: `Bearer ${token}`,
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
"User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async registerTools(register, _getInput) {
|
|
60
|
+
const tools = [
|
|
61
|
+
new RunRegressionTests(this),
|
|
62
|
+
new RunTestCases(this),
|
|
63
|
+
new RunTestsInFunctionalAreas(this),
|
|
64
|
+
new RefineTestCases(this),
|
|
65
|
+
new RefineTestsInFunctionalAreas(this),
|
|
66
|
+
new RefineAllDraftTests(this),
|
|
67
|
+
new ExpandApplicationModel(this),
|
|
68
|
+
new ChatWithQaLead(this),
|
|
69
|
+
new GetTask(this),
|
|
70
|
+
new GetTaskStatus(this),
|
|
71
|
+
new StopTask(this),
|
|
72
|
+
new WaitForTask(this)
|
|
73
|
+
];
|
|
74
|
+
for (const tool of tools) {
|
|
75
|
+
register(tool.specification, tool.handle);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
BearQClient
|
|
81
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
instruction: z.string().min(1).describe(
|
|
5
|
+
"Natural language instruction to send to the BearQ QA lead agent."
|
|
6
|
+
)
|
|
7
|
+
});
|
|
8
|
+
class ChatWithQaLead extends Tool {
|
|
9
|
+
specification = {
|
|
10
|
+
title: "Chat with QA Lead",
|
|
11
|
+
summary: "Sends an open-ended instruction to BearQ's QA lead agent. Use this when no other BearQ tool fits — the QA lead can list, create, and update test cases, manage functional areas, and read the application model, and acts as a general-purpose escape hatch.",
|
|
12
|
+
inputSchema
|
|
13
|
+
};
|
|
14
|
+
handle = async (args) => {
|
|
15
|
+
const { instruction } = inputSchema.parse(args);
|
|
16
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
17
|
+
method: "POST",
|
|
18
|
+
headers: this.client.getHeaders(),
|
|
19
|
+
body: JSON.stringify({ agent: "qa-lead", instruction })
|
|
20
|
+
});
|
|
21
|
+
if (!res.ok)
|
|
22
|
+
throw new ToolError(
|
|
23
|
+
`POST /tasks failed: ${res.status} ${res.statusText}`
|
|
24
|
+
);
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
ChatWithQaLead
|
|
32
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
functionalArea: z.union([z.number().int().positive(), z.string().min(1)]).optional().describe(
|
|
5
|
+
"Functional area to scope the exploration to, by ID or name. Omit to explore the entire application."
|
|
6
|
+
)
|
|
7
|
+
});
|
|
8
|
+
class ExpandApplicationModel extends Tool {
|
|
9
|
+
specification = {
|
|
10
|
+
title: "Expand Application Model",
|
|
11
|
+
summary: "Explores the live application to discover or update its pages and elements in BearQ's application model. Optionally scope to a single functional area.",
|
|
12
|
+
inputSchema
|
|
13
|
+
};
|
|
14
|
+
handle = async (args) => {
|
|
15
|
+
const { functionalArea } = inputSchema.parse(args);
|
|
16
|
+
const body = { agent: "explorer" };
|
|
17
|
+
if (functionalArea !== void 0) body.functionalArea = functionalArea;
|
|
18
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: this.client.getHeaders(),
|
|
21
|
+
body: JSON.stringify(body)
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok)
|
|
24
|
+
throw new ToolError(
|
|
25
|
+
`POST /tasks failed: ${res.status} ${res.statusText}`
|
|
26
|
+
);
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
ExpandApplicationModel
|
|
34
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
taskId: z.number().int().positive().describe("BearQ task ID.")
|
|
5
|
+
});
|
|
6
|
+
class GetTaskStatus extends Tool {
|
|
7
|
+
specification = {
|
|
8
|
+
title: "Get Task Status",
|
|
9
|
+
summary: "Retrieves the status of a task (running / complete / error / cancelled). Cheaper than fetching full task details.",
|
|
10
|
+
inputSchema
|
|
11
|
+
};
|
|
12
|
+
handle = async (args) => {
|
|
13
|
+
const { taskId } = inputSchema.parse(args);
|
|
14
|
+
const res = await fetch(
|
|
15
|
+
`${this.client.getBaseUrl()}/tasks/${taskId}/status`,
|
|
16
|
+
{
|
|
17
|
+
method: "GET",
|
|
18
|
+
headers: this.client.getHeaders()
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
if (!res.ok)
|
|
22
|
+
throw new ToolError(
|
|
23
|
+
`GET /tasks/${taskId}/status failed: ${res.status} ${res.statusText}`
|
|
24
|
+
);
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
GetTaskStatus
|
|
32
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
taskId: z.number().int().positive().describe("BearQ task ID.")
|
|
5
|
+
});
|
|
6
|
+
class GetTask extends Tool {
|
|
7
|
+
specification = {
|
|
8
|
+
title: "Get Task",
|
|
9
|
+
summary: "Retrieves a task's current state, metadata, and activity log. Returns immediately with whatever's available — does not block on the task completing.",
|
|
10
|
+
inputSchema
|
|
11
|
+
};
|
|
12
|
+
handle = async (args) => {
|
|
13
|
+
const { taskId } = inputSchema.parse(args);
|
|
14
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks/${taskId}`, {
|
|
15
|
+
method: "GET",
|
|
16
|
+
headers: this.client.getHeaders()
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok)
|
|
19
|
+
throw new ToolError(
|
|
20
|
+
`GET /tasks/${taskId} failed: ${res.status} ${res.statusText}`
|
|
21
|
+
);
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
GetTask
|
|
29
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({});
|
|
4
|
+
class RefineAllDraftTests extends Tool {
|
|
5
|
+
specification = {
|
|
6
|
+
title: "Refine All Draft Tests",
|
|
7
|
+
summary: "Refines every draft test case in the workspace. Use to push an entire draft backlog forward; may take multiple passes before all drafts are ready to be promoted to regression tests.",
|
|
8
|
+
inputSchema
|
|
9
|
+
};
|
|
10
|
+
handle = async (_args) => {
|
|
11
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
headers: this.client.getHeaders(),
|
|
14
|
+
body: JSON.stringify({ agent: "tester", mode: "refine" })
|
|
15
|
+
});
|
|
16
|
+
if (!res.ok)
|
|
17
|
+
throw new ToolError(
|
|
18
|
+
`POST /tasks failed: ${res.status} ${res.statusText}`
|
|
19
|
+
);
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
RefineAllDraftTests
|
|
27
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
testCaseIds: z.array(z.number().int().positive()).min(1).describe("IDs of BearQ draft test cases to refine.")
|
|
5
|
+
});
|
|
6
|
+
class RefineTestCases extends Tool {
|
|
7
|
+
specification = {
|
|
8
|
+
title: "Refine Test Cases",
|
|
9
|
+
summary: "Refines specific BearQ draft test cases — improves their steps so they can eventually be promoted to regression tests. Use when a draft is incomplete or has drifted from its intent.",
|
|
10
|
+
inputSchema
|
|
11
|
+
};
|
|
12
|
+
handle = async (args) => {
|
|
13
|
+
const { testCaseIds } = inputSchema.parse(args);
|
|
14
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: this.client.getHeaders(),
|
|
17
|
+
body: JSON.stringify({ agent: "tester", mode: "refine", testCaseIds })
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok)
|
|
20
|
+
throw new ToolError(
|
|
21
|
+
`POST /tasks failed: ${res.status} ${res.statusText}`
|
|
22
|
+
);
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
RefineTestCases
|
|
30
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
functionalAreas: z.array(z.union([z.number().int().positive(), z.string().min(1)])).min(1).describe("Functional areas to target, by ID or name.")
|
|
5
|
+
});
|
|
6
|
+
class RefineTestsInFunctionalAreas extends Tool {
|
|
7
|
+
specification = {
|
|
8
|
+
title: "Refine Tests in Functional Areas",
|
|
9
|
+
summary: "Refines every draft test case tagged with one or more functional areas. Functional areas can be given by ID or name. May take multiple passes before drafts are ready to be promoted to regression tests.",
|
|
10
|
+
inputSchema
|
|
11
|
+
};
|
|
12
|
+
handle = async (args) => {
|
|
13
|
+
const { functionalAreas } = inputSchema.parse(args);
|
|
14
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: this.client.getHeaders(),
|
|
17
|
+
body: JSON.stringify({
|
|
18
|
+
agent: "tester",
|
|
19
|
+
mode: "refine",
|
|
20
|
+
functionalAreas
|
|
21
|
+
})
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok)
|
|
24
|
+
throw new ToolError(
|
|
25
|
+
`POST /tasks failed: ${res.status} ${res.statusText}`
|
|
26
|
+
);
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
RefineTestsInFunctionalAreas
|
|
34
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({});
|
|
4
|
+
class RunRegressionTests extends Tool {
|
|
5
|
+
specification = {
|
|
6
|
+
title: "Run Regression Tests",
|
|
7
|
+
summary: "Runs the full BearQ regression suite — every regression-ready test case in the workspace. Use for CI/CD or pre-release smoke.",
|
|
8
|
+
inputSchema
|
|
9
|
+
};
|
|
10
|
+
handle = async (_args) => {
|
|
11
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
headers: this.client.getHeaders(),
|
|
14
|
+
body: JSON.stringify({ agent: "tester", mode: "run" })
|
|
15
|
+
});
|
|
16
|
+
if (!res.ok)
|
|
17
|
+
throw new ToolError(
|
|
18
|
+
`POST /tasks failed: ${res.status} ${res.statusText}`
|
|
19
|
+
);
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
RunRegressionTests
|
|
27
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
testCaseIds: z.array(z.number().int().positive()).min(1).describe("IDs of BearQ regression test cases to run.")
|
|
5
|
+
});
|
|
6
|
+
class RunTestCases extends Tool {
|
|
7
|
+
specification = {
|
|
8
|
+
title: "Run Test Cases",
|
|
9
|
+
summary: "Runs specific BearQ regression test cases by ID. Targets only regression-ready cases — drafts will be rejected.",
|
|
10
|
+
inputSchema
|
|
11
|
+
};
|
|
12
|
+
handle = async (args) => {
|
|
13
|
+
const { testCaseIds } = inputSchema.parse(args);
|
|
14
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: this.client.getHeaders(),
|
|
17
|
+
body: JSON.stringify({ agent: "tester", mode: "run", testCaseIds })
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok)
|
|
20
|
+
throw new ToolError(
|
|
21
|
+
`POST /tasks failed: ${res.status} ${res.statusText}`
|
|
22
|
+
);
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
RunTestCases
|
|
30
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
functionalAreas: z.array(z.union([z.number().int().positive(), z.string().min(1)])).min(1).describe("Functional areas to target, by ID or name.")
|
|
5
|
+
});
|
|
6
|
+
class RunTestsInFunctionalAreas extends Tool {
|
|
7
|
+
specification = {
|
|
8
|
+
title: "Run Tests in Functional Areas",
|
|
9
|
+
summary: "Runs every regression test case tagged with one or more functional areas. Functional areas can be given by ID or name.",
|
|
10
|
+
inputSchema
|
|
11
|
+
};
|
|
12
|
+
handle = async (args) => {
|
|
13
|
+
const { functionalAreas } = inputSchema.parse(args);
|
|
14
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: this.client.getHeaders(),
|
|
17
|
+
body: JSON.stringify({ agent: "tester", mode: "run", functionalAreas })
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok)
|
|
20
|
+
throw new ToolError(
|
|
21
|
+
`POST /tasks failed: ${res.status} ${res.statusText}`
|
|
22
|
+
);
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
RunTestsInFunctionalAreas
|
|
30
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
taskId: z.number().int().positive().describe("BearQ task ID to cancel.")
|
|
5
|
+
});
|
|
6
|
+
class StopTask extends Tool {
|
|
7
|
+
specification = {
|
|
8
|
+
title: "Stop Task",
|
|
9
|
+
summary: "Cancels a running task.",
|
|
10
|
+
inputSchema
|
|
11
|
+
};
|
|
12
|
+
handle = async (args) => {
|
|
13
|
+
const { taskId } = inputSchema.parse(args);
|
|
14
|
+
const res = await fetch(`${this.client.getBaseUrl()}/tasks/${taskId}`, {
|
|
15
|
+
method: "DELETE",
|
|
16
|
+
headers: this.client.getHeaders()
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok)
|
|
19
|
+
throw new ToolError(
|
|
20
|
+
`DELETE /tasks/${taskId} failed: ${res.status} ${res.statusText}`
|
|
21
|
+
);
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
StopTask
|
|
29
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
taskId: z.number().int().positive().describe("BearQ task ID.")
|
|
5
|
+
});
|
|
6
|
+
function parseFrame(frame) {
|
|
7
|
+
let event = "";
|
|
8
|
+
let data = "";
|
|
9
|
+
for (const line of frame.split("\n")) {
|
|
10
|
+
if (line.startsWith(":")) continue;
|
|
11
|
+
if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
12
|
+
else if (line.startsWith("data:")) data = line.slice(5).trim();
|
|
13
|
+
}
|
|
14
|
+
if (!event || !data) return null;
|
|
15
|
+
return { event, data };
|
|
16
|
+
}
|
|
17
|
+
class WaitForTask extends Tool {
|
|
18
|
+
specification = {
|
|
19
|
+
title: "Wait For Task",
|
|
20
|
+
summary: "Blocks until a BearQ task reaches a terminal state (completed / failed / cancelled) or the stream times out, then returns the full ordered sequence of SSE events from the public API (metadata, activityLogEntries, and a terminal done or timeout event) verbatim. Blocks for the lifetime of the task — for a quick check use bearq_get_task_status instead.",
|
|
21
|
+
inputSchema
|
|
22
|
+
};
|
|
23
|
+
handle = async (args, extra) => {
|
|
24
|
+
const { taskId } = inputSchema.parse(args);
|
|
25
|
+
const url = `${this.client.getBaseUrl()}/tasks/${taskId}/stream`;
|
|
26
|
+
const res = await fetch(url, {
|
|
27
|
+
headers: { ...this.client.getHeaders(), Accept: "text/event-stream" }
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok)
|
|
30
|
+
throw new ToolError(
|
|
31
|
+
`GET /tasks/${taskId}/stream failed: ${res.status} ${res.statusText}`
|
|
32
|
+
);
|
|
33
|
+
if (!res.body)
|
|
34
|
+
throw new ToolError(`GET /tasks/${taskId}/stream: no response body`);
|
|
35
|
+
const progressToken = extra?._meta?.progressToken;
|
|
36
|
+
let progress = 0;
|
|
37
|
+
const notify = async (event, data) => {
|
|
38
|
+
if (progressToken === void 0) return;
|
|
39
|
+
await extra.sendNotification({
|
|
40
|
+
method: "notifications/progress",
|
|
41
|
+
params: {
|
|
42
|
+
progressToken,
|
|
43
|
+
progress: ++progress,
|
|
44
|
+
message: JSON.stringify({ event, data })
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const events = [];
|
|
49
|
+
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
50
|
+
let buffer = "";
|
|
51
|
+
try {
|
|
52
|
+
while (true) {
|
|
53
|
+
const { done, value } = await reader.read();
|
|
54
|
+
if (done) break;
|
|
55
|
+
buffer += value;
|
|
56
|
+
let boundary = buffer.indexOf("\n\n");
|
|
57
|
+
while (boundary !== -1) {
|
|
58
|
+
const frame = buffer.slice(0, boundary);
|
|
59
|
+
buffer = buffer.slice(boundary + 2);
|
|
60
|
+
boundary = buffer.indexOf("\n\n");
|
|
61
|
+
const parsed = parseFrame(frame);
|
|
62
|
+
if (!parsed) continue;
|
|
63
|
+
const data = JSON.parse(parsed.data);
|
|
64
|
+
events.push({ event: parsed.event, data });
|
|
65
|
+
await notify(parsed.event, data);
|
|
66
|
+
if (parsed.event === "done" || parsed.event === "timeout") {
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: "text", text: JSON.stringify({ events }) }]
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} finally {
|
|
74
|
+
reader.releaseLock();
|
|
75
|
+
}
|
|
76
|
+
throw new ToolError(
|
|
77
|
+
`GET /tasks/${taskId}/stream closed without a done or timeout event`
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
WaitForTask
|
|
83
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BearQClient } from "../bearq/client.js";
|
|
1
2
|
import { BugsnagClient } from "../bugsnag/client.js";
|
|
2
3
|
import { CollaboratorClient } from "../collaborator/client.js";
|
|
3
4
|
import { PactflowClient } from "../pactflow/client.js";
|
|
@@ -7,6 +8,7 @@ import { ReflectClient } from "../reflect/client.js";
|
|
|
7
8
|
import { SwaggerClient } from "../swagger/client.js";
|
|
8
9
|
import { ZephyrClient } from "../zephyr/client.js";
|
|
9
10
|
import { clientRegistry } from "./client-registry.js";
|
|
11
|
+
clientRegistry.register(new BearQClient());
|
|
10
12
|
clientRegistry.register(new ReflectClient());
|
|
11
13
|
clientRegistry.register(new BugsnagClient());
|
|
12
14
|
clientRegistry.register(new SwaggerClient());
|
package/dist/package.json.js
CHANGED
|
@@ -308,7 +308,7 @@ const AdminUserIdSchema = z.object({
|
|
|
308
308
|
userId: z.string().describe("UUID of the user")
|
|
309
309
|
});
|
|
310
310
|
const CreateAdminUserSchema = z.object({
|
|
311
|
-
email: z.string().
|
|
311
|
+
email: z.string().describe("Email address of the new user"),
|
|
312
312
|
name: z.string().describe("Display name of the new user"),
|
|
313
313
|
firstName: z.string().optional().describe("First name"),
|
|
314
314
|
lastName: z.string().optional().describe("Last name"),
|
|
@@ -318,7 +318,7 @@ const CreateAdminUserSchema = z.object({
|
|
|
318
318
|
const UpdateAdminUserSchema = z.object({
|
|
319
319
|
userId: z.string().describe("UUID of the user to update"),
|
|
320
320
|
active: z.boolean().optional().describe("Whether the user is active"),
|
|
321
|
-
email: z.string().
|
|
321
|
+
email: z.string().optional().describe("New email address"),
|
|
322
322
|
firstName: z.string().optional().describe("First name"),
|
|
323
323
|
lastName: z.string().optional().describe("Last name"),
|
|
324
324
|
name: z.string().optional().describe("Display name")
|
|
@@ -326,7 +326,7 @@ const UpdateAdminUserSchema = z.object({
|
|
|
326
326
|
const InviteUsersSchema = z.object({
|
|
327
327
|
users: z.array(
|
|
328
328
|
z.object({
|
|
329
|
-
email: z.string().
|
|
329
|
+
email: z.string().describe("Email address"),
|
|
330
330
|
name: z.string().min(1).describe("Display name")
|
|
331
331
|
})
|
|
332
332
|
).min(1).describe("List of users to invite")
|