@smartbear/mcp 0.22.0 → 0.23.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 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}",
@@ -109,6 +112,18 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
109
112
  }
110
113
  },
111
114
  "inputs": [
115
+ {
116
+ "id": "bearq_api_token",
117
+ "type": "promptString",
118
+ "description": "BearQ workspace API token - leave blank to disable BearQ tools",
119
+ "password": true
120
+ },
121
+ {
122
+ "id": "bearq_api_base_url",
123
+ "type": "promptString",
124
+ "description": "BearQ API base URL - leave blank to use the default (https://api.bearq.smartbear.com)",
125
+ "password": false
126
+ },
112
127
  {
113
128
  "id": "bugsnag_auth_token",
114
129
  "type": "promptString",
@@ -248,6 +263,7 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
248
263
  "@smartbear/mcp@latest"
249
264
  ],
250
265
  "env": {
266
+ "BEARQ_API_TOKEN": "your_bearq_api_token",
251
267
  "BUGSNAG_AUTH_TOKEN": "your_personal_auth_token",
252
268
  "BUGSNAG_PROJECT_API_KEY": "your_project_api_key",
253
269
  "REFLECT_API_TOKEN": "your_reflect_token",
@@ -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,6 @@
1
+ const DEFAULT_API_BASE_URL = "https://api.bearq.smartbear.com";
2
+ const AUTHORIZATION_HEADER = "authorization";
3
+ export {
4
+ AUTHORIZATION_HEADER,
5
+ DEFAULT_API_BASE_URL
6
+ };
@@ -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());
@@ -1,4 +1,4 @@
1
- const version = "0.22.0";
1
+ const version = "0.23.0";
2
2
  const config = { "mcpServerName": "SmartBear MCP Server" };
3
3
  const packageJson = {
4
4
  version,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartbear/mcp",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "MCP server for interacting SmartBear Products",
5
5
  "keywords": [
6
6
  "smartbear",