@smartbear/mcp 0.27.2 → 0.29.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 +1 -1
- package/dist/bearq/client.js +5 -3
- package/dist/bearq/tool/environments/list-environments.js +28 -0
- package/dist/bearq/tool/tasks/run-regression-tests.js +10 -3
- package/dist/bearq/tool/tasks/run-test-cases.js +12 -3
- package/dist/bearq/tool/tasks/run-tests-in-functional-areas.js +12 -3
- package/dist/bugsnag/client.js +2 -2
- package/dist/collaborator/client.js +5 -2
- package/dist/common/client-identity.js +20 -0
- package/dist/common/info.js +21 -1
- package/dist/common/initialize.js +29 -0
- package/dist/common/request-context.js +7 -0
- package/dist/common/server.js +39 -0
- package/dist/common/transport-http.js +15 -26
- package/dist/common/transport-stdio.js +2 -13
- package/dist/package.json.js +1 -1
- package/dist/pactflow/client.js +11 -4
- package/dist/qmetry/client/api/client-api.js +2 -2
- package/dist/qmetry/client/automation.js +2 -2
- package/dist/qtm4j/http/auth-service.js +2 -2
- package/dist/reflect/client.js +2 -2
- package/dist/swagger/client/api.js +7 -5
- package/dist/swagger/client/functional-testing-api.js +188 -6
- package/dist/swagger/client/functional-testing-tools.js +49 -2
- package/dist/swagger/client/functional-testing-types.js +21 -0
- package/dist/swagger/client/portal-types.js +4 -1
- package/dist/swagger/client.js +19 -0
- package/dist/zephyr/common/auth-service.js +2 -2
- package/dist/zephyr/common/rest-api-schemas.js +2200 -103
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ See individual guides for suggested prompts and supported tools and resources:
|
|
|
35
35
|
- [Portal](https://developer.smartbear.com/smartbear-mcp/docs/swagger-portal-integration) - Portal and product management capabilities
|
|
36
36
|
- [Studio](https://developer.smartbear.com/smartbear-mcp/docs/swagger-studio-integration) - API and Domain management capabilities, including AI-powered API generation from prompts and automatic standardization
|
|
37
37
|
- [Contract Testing (PactFlow)](https://developer.smartbear.com/pactflow/default/getting-started) - Contract testing capabilities
|
|
38
|
-
- [Functional Testing](https://developer.smartbear.com/smartbear-mcp/docs/functional-testing-integration) - API test discovery capabilities
|
|
38
|
+
- [Functional Testing](https://developer.smartbear.com/smartbear-mcp/docs/swagger-functional-testing-integration) - API test discovery capabilities
|
|
39
39
|
- [QMetry](https://developer.smartbear.com/smartbear-mcp/docs/qmetry-integration) - QMetry Test Management capabilities
|
|
40
40
|
- [Zephyr](https://developer.smartbear.com/smartbear-mcp/docs/zephyr-integration) - Zephyr Test Management capabilities
|
|
41
41
|
- [Collaborator](https://developer.smartbear.com/smartbear-mcp/docs/collaborator-integration) - Review and Remote System Configuration management capabilities
|
package/dist/bearq/client.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { getUserAgent } from "../common/info.js";
|
|
3
3
|
import { getRequestHeader } from "../common/request-context.js";
|
|
4
4
|
import { ToolError } from "../common/tools.js";
|
|
5
5
|
import { DEFAULT_API_BASE_URL, AUTHORIZATION_HEADER } from "./config/constants.js";
|
|
6
|
+
import { ListEnvironments } from "./tool/environments/list-environments.js";
|
|
6
7
|
import { ChatWithQaLead } from "./tool/tasks/chat-with-qa-lead.js";
|
|
7
8
|
import { ExpandApplicationModel } from "./tool/tasks/expand-application-model.js";
|
|
8
9
|
import { GetTask } from "./tool/tasks/get-task.js";
|
|
@@ -53,7 +54,7 @@ class BearQClient {
|
|
|
53
54
|
return {
|
|
54
55
|
Authorization: `Bearer ${token}`,
|
|
55
56
|
"Content-Type": "application/json",
|
|
56
|
-
"User-Agent":
|
|
57
|
+
"User-Agent": getUserAgent()
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
60
|
async registerTools(register, _getInput) {
|
|
@@ -69,7 +70,8 @@ class BearQClient {
|
|
|
69
70
|
new GetTask(this),
|
|
70
71
|
new GetTaskStatus(this),
|
|
71
72
|
new StopTask(this),
|
|
72
|
-
new WaitForTask(this)
|
|
73
|
+
new WaitForTask(this),
|
|
74
|
+
new ListEnvironments(this)
|
|
73
75
|
];
|
|
74
76
|
for (const tool of tools) {
|
|
75
77
|
register(tool.specification, tool.handle);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
+
const inputSchema = z.object({});
|
|
4
|
+
class ListEnvironments extends Tool {
|
|
5
|
+
specification = {
|
|
6
|
+
title: "List Environments",
|
|
7
|
+
toolset: "Environments",
|
|
8
|
+
summary: "Lists the environments configured in the workspace. Use this to discover valid environment names to pass to the test-running tools, and to identify the workspace default.",
|
|
9
|
+
inputSchema,
|
|
10
|
+
readOnly: true
|
|
11
|
+
};
|
|
12
|
+
handle = async (_args) => {
|
|
13
|
+
const res = await fetch(`${this.client.getBaseUrl()}/environments`, {
|
|
14
|
+
method: "GET",
|
|
15
|
+
headers: this.client.getHeaders()
|
|
16
|
+
});
|
|
17
|
+
if (!res.ok)
|
|
18
|
+
throw new ToolError(
|
|
19
|
+
`GET /environments failed: ${res.status} ${res.statusText}`
|
|
20
|
+
);
|
|
21
|
+
return {
|
|
22
|
+
content: [{ type: "text", text: JSON.stringify(await res.json()) }]
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
ListEnvironments
|
|
28
|
+
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
|
-
const inputSchema = z.object({
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
environment: z.string().min(1).optional().describe(
|
|
5
|
+
"Target environment name to run tests against. Omit to use the workspace default."
|
|
6
|
+
)
|
|
7
|
+
});
|
|
4
8
|
class RunRegressionTests extends Tool {
|
|
5
9
|
specification = {
|
|
6
10
|
title: "Run Regression Tests",
|
|
@@ -8,11 +12,14 @@ class RunRegressionTests extends Tool {
|
|
|
8
12
|
summary: "Runs the full BearQ regression suite — every regression-ready test case in the workspace. Use for CI/CD or pre-release smoke.",
|
|
9
13
|
inputSchema
|
|
10
14
|
};
|
|
11
|
-
handle = async (
|
|
15
|
+
handle = async (args) => {
|
|
16
|
+
const { environment } = inputSchema.parse(args);
|
|
17
|
+
const body = { agent: "tester", mode: "run" };
|
|
18
|
+
if (environment !== void 0) body.environment = environment;
|
|
12
19
|
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
13
20
|
method: "POST",
|
|
14
21
|
headers: this.client.getHeaders(),
|
|
15
|
-
body: JSON.stringify(
|
|
22
|
+
body: JSON.stringify(body)
|
|
16
23
|
});
|
|
17
24
|
if (!res.ok)
|
|
18
25
|
throw new ToolError(
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
3
|
const inputSchema = z.object({
|
|
4
|
-
testCaseIds: z.array(z.number().int().positive()).min(1).describe("IDs of BearQ regression test cases to run.")
|
|
4
|
+
testCaseIds: z.array(z.number().int().positive()).min(1).describe("IDs of BearQ regression test cases to run."),
|
|
5
|
+
environment: z.string().min(1).optional().describe(
|
|
6
|
+
"Target environment name to run tests against. Omit to use the workspace default."
|
|
7
|
+
)
|
|
5
8
|
});
|
|
6
9
|
class RunTestCases extends Tool {
|
|
7
10
|
specification = {
|
|
@@ -11,11 +14,17 @@ class RunTestCases extends Tool {
|
|
|
11
14
|
inputSchema
|
|
12
15
|
};
|
|
13
16
|
handle = async (args) => {
|
|
14
|
-
const { testCaseIds } = inputSchema.parse(args);
|
|
17
|
+
const { testCaseIds, environment } = inputSchema.parse(args);
|
|
18
|
+
const body = {
|
|
19
|
+
agent: "tester",
|
|
20
|
+
mode: "run",
|
|
21
|
+
testCaseIds
|
|
22
|
+
};
|
|
23
|
+
if (environment !== void 0) body.environment = environment;
|
|
15
24
|
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
16
25
|
method: "POST",
|
|
17
26
|
headers: this.client.getHeaders(),
|
|
18
|
-
body: JSON.stringify(
|
|
27
|
+
body: JSON.stringify(body)
|
|
19
28
|
});
|
|
20
29
|
if (!res.ok)
|
|
21
30
|
throw new ToolError(
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { Tool, ToolError } from "../../../common/tools.js";
|
|
3
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.")
|
|
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
|
+
environment: z.string().min(1).optional().describe(
|
|
6
|
+
"Target environment name to run tests against. Omit to use the workspace default."
|
|
7
|
+
)
|
|
5
8
|
});
|
|
6
9
|
class RunTestsInFunctionalAreas extends Tool {
|
|
7
10
|
specification = {
|
|
@@ -11,11 +14,17 @@ class RunTestsInFunctionalAreas extends Tool {
|
|
|
11
14
|
inputSchema
|
|
12
15
|
};
|
|
13
16
|
handle = async (args) => {
|
|
14
|
-
const { functionalAreas } = inputSchema.parse(args);
|
|
17
|
+
const { functionalAreas, environment } = inputSchema.parse(args);
|
|
18
|
+
const body = {
|
|
19
|
+
agent: "tester",
|
|
20
|
+
mode: "run",
|
|
21
|
+
functionalAreas
|
|
22
|
+
};
|
|
23
|
+
if (environment !== void 0) body.environment = environment;
|
|
15
24
|
const res = await fetch(`${this.client.getBaseUrl()}/tasks`, {
|
|
16
25
|
method: "POST",
|
|
17
26
|
headers: this.client.getHeaders(),
|
|
18
|
-
body: JSON.stringify(
|
|
27
|
+
body: JSON.stringify(body)
|
|
19
28
|
});
|
|
20
29
|
if (!res.ok)
|
|
21
30
|
throw new ToolError(
|
package/dist/bugsnag/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { getUserAgent } from "../common/info.js";
|
|
3
3
|
import { getRequestHeader } from "../common/request-context.js";
|
|
4
4
|
import { ToolError } from "../common/tools.js";
|
|
5
5
|
import { CurrentUserAPI } from "./client/api/CurrentUser.js";
|
|
@@ -123,7 +123,7 @@ class BugsnagClient {
|
|
|
123
123
|
);
|
|
124
124
|
},
|
|
125
125
|
headers: {
|
|
126
|
-
"User-Agent":
|
|
126
|
+
"User-Agent": getUserAgent(),
|
|
127
127
|
"Content-Type": "application/json",
|
|
128
128
|
"X-Bugsnag-API": "true",
|
|
129
129
|
"X-Version": "2"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { getUserAgent } from "../common/info.js";
|
|
3
3
|
import { getRequestHeader } from "../common/request-context.js";
|
|
4
4
|
const ConfigurationSchema = z.object({
|
|
5
5
|
base_url: z.url().describe("Collaborator server base URL"),
|
|
@@ -48,7 +48,10 @@ class CollaboratorClient {
|
|
|
48
48
|
];
|
|
49
49
|
const response = await fetch(url, {
|
|
50
50
|
method: "POST",
|
|
51
|
-
headers: {
|
|
51
|
+
headers: {
|
|
52
|
+
"Content-Type": "application/json",
|
|
53
|
+
"User-Agent": getUserAgent()
|
|
54
|
+
},
|
|
52
55
|
body: JSON.stringify(body)
|
|
53
56
|
});
|
|
54
57
|
if (!response.ok) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getRequestContext } from "./request-context.js";
|
|
2
|
+
function toClientIdentity(info, protocolVersion) {
|
|
3
|
+
return {
|
|
4
|
+
name: info?.name,
|
|
5
|
+
version: info?.version,
|
|
6
|
+
protocolVersion
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
let processClientIdentity;
|
|
10
|
+
function setProcessClientIdentity(identity) {
|
|
11
|
+
processClientIdentity = identity;
|
|
12
|
+
}
|
|
13
|
+
function getCurrentClientIdentity() {
|
|
14
|
+
return getRequestContext()?.mcpClient ?? processClientIdentity;
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
getCurrentClientIdentity,
|
|
18
|
+
setProcessClientIdentity,
|
|
19
|
+
toClientIdentity
|
|
20
|
+
};
|
package/dist/common/info.js
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import packageJson from "../package.json.js";
|
|
2
|
+
import { getCurrentClientIdentity } from "./client-identity.js";
|
|
2
3
|
const MCP_SERVER_NAME = packageJson.config.mcpServerName;
|
|
3
4
|
const MCP_SERVER_VERSION = packageJson.version;
|
|
4
5
|
const MCP_TRANSPORT = process.env.MCP_TRANSPORT?.toLowerCase().trim() || "stdio";
|
|
5
6
|
const USER_AGENT = `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION} ${MCP_TRANSPORT}`;
|
|
7
|
+
function sanitizeUserAgentToken(value) {
|
|
8
|
+
return value.replace(/[()\r\n;]/g, "").trim();
|
|
9
|
+
}
|
|
10
|
+
function clientIdentitySuffix(identity) {
|
|
11
|
+
if (!identity?.name) {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
const clientName = sanitizeUserAgentToken(identity.name) || "unknown";
|
|
15
|
+
const version = identity.version ? sanitizeUserAgentToken(identity.version) || "unknown" : "unknown";
|
|
16
|
+
return ` (client: ${clientName}; clientVersion: ${version})`;
|
|
17
|
+
}
|
|
18
|
+
function appendClientIdentity(baseUserAgent) {
|
|
19
|
+
return baseUserAgent + clientIdentitySuffix(getCurrentClientIdentity());
|
|
20
|
+
}
|
|
21
|
+
function getUserAgent() {
|
|
22
|
+
return appendClientIdentity(USER_AGENT);
|
|
23
|
+
}
|
|
6
24
|
export {
|
|
7
25
|
MCP_SERVER_NAME,
|
|
8
26
|
MCP_SERVER_VERSION,
|
|
9
27
|
MCP_TRANSPORT,
|
|
10
|
-
USER_AGENT
|
|
28
|
+
USER_AGENT,
|
|
29
|
+
appendClientIdentity,
|
|
30
|
+
getUserAgent
|
|
11
31
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { toClientIdentity, setProcessClientIdentity } from "./client-identity.js";
|
|
2
|
+
function handleInitializeMessage(server, message) {
|
|
3
|
+
if (typeof message !== "object" || message === null || !("method" in message) || message.method !== "initialize") {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
const params = message.params;
|
|
7
|
+
const identity = toClientIdentity(
|
|
8
|
+
params?.clientInfo,
|
|
9
|
+
params?.protocolVersion
|
|
10
|
+
);
|
|
11
|
+
server.setMcpClientIdentity(identity);
|
|
12
|
+
setProcessClientIdentity(identity);
|
|
13
|
+
const clientInfo = params?.clientInfo;
|
|
14
|
+
if (clientInfo) {
|
|
15
|
+
server.setClientInfo(clientInfo);
|
|
16
|
+
}
|
|
17
|
+
if (params?.protocolVersion === "2025-11-25") {
|
|
18
|
+
const clientCapabilities = params.capabilities ?? {};
|
|
19
|
+
if (Object.hasOwn(clientCapabilities, "sampling")) {
|
|
20
|
+
server.setSamplingSupported(true);
|
|
21
|
+
}
|
|
22
|
+
if (Object.hasOwn(clientCapabilities, "elicitation")) {
|
|
23
|
+
server.setElicitationSupported(true);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
handleInitializeMessage
|
|
29
|
+
};
|
|
@@ -6,6 +6,12 @@ function withRequestContext(req, fn) {
|
|
|
6
6
|
function getRequestContext() {
|
|
7
7
|
return requestContextStorage.getStore();
|
|
8
8
|
}
|
|
9
|
+
function setRequestMcpClient(identity) {
|
|
10
|
+
const context = getRequestContext();
|
|
11
|
+
if (context) {
|
|
12
|
+
context.mcpClient = identity;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
9
15
|
function getRequestHeader(name) {
|
|
10
16
|
const context = getRequestContext();
|
|
11
17
|
if (!context?.headers) return void 0;
|
|
@@ -16,5 +22,6 @@ export {
|
|
|
16
22
|
getRequestContext,
|
|
17
23
|
getRequestHeader,
|
|
18
24
|
requestContextStorage,
|
|
25
|
+
setRequestMcpClient,
|
|
19
26
|
withRequestContext
|
|
20
27
|
};
|
package/dist/common/server.js
CHANGED
|
@@ -2,6 +2,7 @@ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mc
|
|
|
2
2
|
import { ZodObject, ZodIntersection } from "zod";
|
|
3
3
|
import Bugsnag from "./bugsnag.js";
|
|
4
4
|
import { CacheService } from "./cache.js";
|
|
5
|
+
import { toClientIdentity } from "./client-identity.js";
|
|
5
6
|
import { MCP_SERVER_VERSION, MCP_SERVER_NAME } from "./info.js";
|
|
6
7
|
import { executeElicitationOrPolyfill, isElicitationPolyfillResult } from "./pollyfills.js";
|
|
7
8
|
import { ToolError } from "./tools.js";
|
|
@@ -10,8 +11,10 @@ class SmartBearMcpServer extends McpServer {
|
|
|
10
11
|
cache;
|
|
11
12
|
samplingSupported = false;
|
|
12
13
|
elicitationSupported = false;
|
|
14
|
+
clientInfo;
|
|
13
15
|
clients = [];
|
|
14
16
|
enabledToolsets;
|
|
17
|
+
mcpClientIdentity;
|
|
15
18
|
constructor(enabledToolsets) {
|
|
16
19
|
super(
|
|
17
20
|
{
|
|
@@ -48,9 +51,42 @@ class SmartBearMcpServer extends McpServer {
|
|
|
48
51
|
isElicitationSupported() {
|
|
49
52
|
return this.elicitationSupported;
|
|
50
53
|
}
|
|
54
|
+
setClientInfo(info) {
|
|
55
|
+
this.clientInfo = info;
|
|
56
|
+
}
|
|
57
|
+
getClientInfo() {
|
|
58
|
+
return this.clientInfo;
|
|
59
|
+
}
|
|
51
60
|
getClients() {
|
|
52
61
|
return this.clients;
|
|
53
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Record the MCP client identity reported in the `initialize` handshake.
|
|
65
|
+
* Captured once per session by the transport layer.
|
|
66
|
+
*/
|
|
67
|
+
setMcpClientIdentity(identity) {
|
|
68
|
+
this.mcpClientIdentity = identity;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Return the MCP client identity for this session. Prefers the value captured
|
|
72
|
+
* at `initialize`; falls back to the SDK's `getClientVersion()` so callers
|
|
73
|
+
* still get an answer if the explicit capture was skipped.
|
|
74
|
+
*/
|
|
75
|
+
getMcpClientIdentity() {
|
|
76
|
+
return this.mcpClientIdentity ?? toClientIdentity(this.server.getClientVersion());
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Attach MCP client attribution to a Bugsnag event so errors can be segmented
|
|
80
|
+
* by originating client/marketplace.
|
|
81
|
+
*/
|
|
82
|
+
addClientMetadata(event) {
|
|
83
|
+
const identity = this.getMcpClientIdentity();
|
|
84
|
+
event.addMetadata("mcpClient", {
|
|
85
|
+
mcp_client_name: identity.name ?? null,
|
|
86
|
+
mcp_client_version: identity.version ?? null,
|
|
87
|
+
mcp_protocol_version: identity.protocolVersion ?? null
|
|
88
|
+
});
|
|
89
|
+
}
|
|
54
90
|
async cleanupSession(mcpSessionId) {
|
|
55
91
|
for (const client of this.clients) {
|
|
56
92
|
await client.cleanupSession?.(mcpSessionId);
|
|
@@ -106,6 +142,7 @@ class SmartBearMcpServer extends McpServer {
|
|
|
106
142
|
} else {
|
|
107
143
|
Bugsnag.notify(e, (event) => {
|
|
108
144
|
event.addMetadata("app", { tool: toolName });
|
|
145
|
+
this.addClientMetadata(event);
|
|
109
146
|
event.unhandled = true;
|
|
110
147
|
});
|
|
111
148
|
}
|
|
@@ -156,6 +193,7 @@ ${result.instructions}`
|
|
|
156
193
|
resource: resourceName,
|
|
157
194
|
url: url2
|
|
158
195
|
});
|
|
196
|
+
this.addClientMetadata(event);
|
|
159
197
|
event.unhandled = true;
|
|
160
198
|
});
|
|
161
199
|
throw e;
|
|
@@ -181,6 +219,7 @@ ${result.instructions}`
|
|
|
181
219
|
event.addMetadata("app", {
|
|
182
220
|
prompt: this.getCapabilityName(client, params.title)
|
|
183
221
|
});
|
|
222
|
+
this.addClientMetadata(event);
|
|
184
223
|
event.unhandled = true;
|
|
185
224
|
});
|
|
186
225
|
throw e;
|
|
@@ -5,7 +5,8 @@ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
|
5
5
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
6
6
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
7
7
|
import { clientRegistry } from "./client-registry.js";
|
|
8
|
-
import {
|
|
8
|
+
import { handleInitializeMessage } from "./initialize.js";
|
|
9
|
+
import { withRequestContext, setRequestMcpClient } from "./request-context.js";
|
|
9
10
|
import { SmartBearMcpServer } from "./server.js";
|
|
10
11
|
import { isDraining, registerShutdownHandler } from "./shutdown.js";
|
|
11
12
|
import { getEnvVarName } from "./transport-stdio.js";
|
|
@@ -211,19 +212,7 @@ async function createNewTransport(req, res, transports) {
|
|
|
211
212
|
transports.set(newSessionId, { server, transport });
|
|
212
213
|
}
|
|
213
214
|
});
|
|
214
|
-
transport.onmessage = (message) =>
|
|
215
|
-
if ("method" in message && message.method === "initialize") {
|
|
216
|
-
if (message.params?.protocolVersion === "2025-11-25") {
|
|
217
|
-
const clientCapabilities = message.params?.capabilities;
|
|
218
|
-
if (Object.hasOwn(clientCapabilities, "sampling")) {
|
|
219
|
-
server.setSamplingSupported(true);
|
|
220
|
-
}
|
|
221
|
-
if (Object.hasOwn(clientCapabilities, "elicitation")) {
|
|
222
|
-
server.setElicitationSupported(true);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
};
|
|
215
|
+
transport.onmessage = (message) => handleInitializeMessage(server, message);
|
|
227
216
|
transport.onclose = () => {
|
|
228
217
|
if (transport.sessionId) {
|
|
229
218
|
console.log(`[MCP] Session closed: ${transport.sessionId}`);
|
|
@@ -280,10 +269,13 @@ async function handleStreamableHttpRequest(req, res, transports) {
|
|
|
280
269
|
);
|
|
281
270
|
return;
|
|
282
271
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
272
|
+
const sessionServer = sessionId ? transports.get(sessionId)?.server : void 0;
|
|
273
|
+
await withRequestContext(req, async () => {
|
|
274
|
+
if (sessionServer) {
|
|
275
|
+
setRequestMcpClient(sessionServer.getMcpClientIdentity());
|
|
276
|
+
}
|
|
277
|
+
return await transport.handleRequest(req, res, parsedBody);
|
|
278
|
+
});
|
|
287
279
|
} catch (error) {
|
|
288
280
|
console.error("Error handling StreamableHTTP request:", error);
|
|
289
281
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
@@ -296,6 +288,7 @@ async function handleLegacySseRequest(req, res, transports) {
|
|
|
296
288
|
return;
|
|
297
289
|
}
|
|
298
290
|
const transport = new SSEServerTransport("/message", res);
|
|
291
|
+
transport.onmessage = (message) => handleInitializeMessage(server, message);
|
|
299
292
|
transports.set(transport.sessionId, { server, transport });
|
|
300
293
|
res.on("close", () => {
|
|
301
294
|
transports.delete(transport.sessionId);
|
|
@@ -328,14 +321,10 @@ async function handleLegacyMessageRequest(req, res, url, transports) {
|
|
|
328
321
|
req.on("end", async () => {
|
|
329
322
|
try {
|
|
330
323
|
const parsedBody = JSON.parse(body);
|
|
331
|
-
await withRequestContext(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
res,
|
|
336
|
-
parsedBody
|
|
337
|
-
)
|
|
338
|
-
);
|
|
324
|
+
await withRequestContext(req, async () => {
|
|
325
|
+
setRequestMcpClient(session.server.getMcpClientIdentity());
|
|
326
|
+
return await session.transport.handlePostMessage(req, res, parsedBody);
|
|
327
|
+
});
|
|
339
328
|
} catch (error) {
|
|
340
329
|
console.error("Error handling POST message:", error);
|
|
341
330
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
@@ -2,6 +2,7 @@ import { enableCompileCache } from "node:module";
|
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { clientRegistry } from "./client-registry.js";
|
|
4
4
|
import { USER_AGENT } from "./info.js";
|
|
5
|
+
import { handleInitializeMessage } from "./initialize.js";
|
|
5
6
|
import { SmartBearMcpServer } from "./server.js";
|
|
6
7
|
import { registerShutdownHandler } from "./shutdown.js";
|
|
7
8
|
import { isOptionalType, getTypeDescription } from "./zod-utils.js";
|
|
@@ -57,19 +58,7 @@ ${message.join("\n")}` : "No clients support environment variable configuration.
|
|
|
57
58
|
console.error("[MCP][shutdown] Error closing stdio transport:", err);
|
|
58
59
|
}
|
|
59
60
|
});
|
|
60
|
-
transport.onmessage = (message) =>
|
|
61
|
-
if ("method" in message && message.method === "initialize") {
|
|
62
|
-
if (message.params?.protocolVersion === "2025-11-25") {
|
|
63
|
-
const clientCapabilities = message.params?.capabilities;
|
|
64
|
-
if (Object.hasOwn(clientCapabilities, "sampling")) {
|
|
65
|
-
server.setSamplingSupported(true);
|
|
66
|
-
}
|
|
67
|
-
if (Object.hasOwn(clientCapabilities, "elicitation")) {
|
|
68
|
-
server.setElicitationSupported(true);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
};
|
|
61
|
+
transport.onmessage = (message) => handleInitializeMessage(server, message);
|
|
73
62
|
await server.connect(transport);
|
|
74
63
|
}
|
|
75
64
|
function getEnvVarName(clientPrefix, key) {
|
package/dist/package.json.js
CHANGED
package/dist/pactflow/client.js
CHANGED
|
@@ -170,6 +170,10 @@ class PactflowClient {
|
|
|
170
170
|
if (Array.isArray(contextToken)) {
|
|
171
171
|
contextToken = contextToken[0];
|
|
172
172
|
}
|
|
173
|
+
const clientInfo = this._server?.getClientInfo();
|
|
174
|
+
const sourceApplicationHeader = {
|
|
175
|
+
SOURCE_APPLICATION: clientInfo ? `${clientInfo.name}/${clientInfo.version}` : "unknown"
|
|
176
|
+
};
|
|
173
177
|
if (contextToken) {
|
|
174
178
|
let authHeader = contextToken;
|
|
175
179
|
if (!contextToken.startsWith("Basic ") && !contextToken.startsWith("Bearer ")) {
|
|
@@ -178,7 +182,8 @@ class PactflowClient {
|
|
|
178
182
|
return {
|
|
179
183
|
Authorization: authHeader,
|
|
180
184
|
"Content-Type": "application/json",
|
|
181
|
-
"User-Agent": USER_AGENT
|
|
185
|
+
"User-Agent": USER_AGENT,
|
|
186
|
+
...sourceApplicationHeader
|
|
182
187
|
};
|
|
183
188
|
}
|
|
184
189
|
if (this.token) {
|
|
@@ -189,14 +194,16 @@ class PactflowClient {
|
|
|
189
194
|
return {
|
|
190
195
|
Authorization: authHeader,
|
|
191
196
|
"Content-Type": "application/json",
|
|
192
|
-
"User-Agent": USER_AGENT
|
|
197
|
+
"User-Agent": USER_AGENT,
|
|
198
|
+
...sourceApplicationHeader
|
|
193
199
|
};
|
|
194
200
|
} else if (this.username && this.password) {
|
|
195
201
|
const authString = `${this.username}:${this.password}`;
|
|
196
202
|
return {
|
|
197
203
|
Authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
|
|
198
204
|
"Content-Type": "application/json",
|
|
199
|
-
"User-Agent": USER_AGENT
|
|
205
|
+
"User-Agent": USER_AGENT,
|
|
206
|
+
...sourceApplicationHeader
|
|
200
207
|
};
|
|
201
208
|
}
|
|
202
209
|
return void 0;
|
|
@@ -1877,7 +1884,7 @@ class PactflowClient {
|
|
|
1877
1884
|
`${this.baseUrl}/admin/teams/${encodeURIComponent(teamId)}/users`,
|
|
1878
1885
|
{
|
|
1879
1886
|
method: "PUT",
|
|
1880
|
-
body: { uuids },
|
|
1887
|
+
body: { users: uuids },
|
|
1881
1888
|
errorContext: "Set Team Users"
|
|
1882
1889
|
}
|
|
1883
1890
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getUserAgent } from "../../../common/info.js";
|
|
2
2
|
import { QMETRY_DEFAULTS } from "../../config/constants.js";
|
|
3
3
|
import { handleQMetryFetchError, handleQMetryApiError } from "./error-handler.js";
|
|
4
4
|
async function qmetryRequest({
|
|
@@ -16,7 +16,7 @@ async function qmetryRequest({
|
|
|
16
16
|
const headers = {
|
|
17
17
|
apikey: token,
|
|
18
18
|
project: project || QMETRY_DEFAULTS.PROJECT_KEY,
|
|
19
|
-
"User-Agent":
|
|
19
|
+
"User-Agent": getUserAgent(),
|
|
20
20
|
"qmetry-source": "smartbear-mcp"
|
|
21
21
|
};
|
|
22
22
|
if (scopeId !== void 0) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getUserAgent } from "../../common/info.js";
|
|
2
2
|
import { QMETRY_DEFAULTS } from "../config/constants.js";
|
|
3
3
|
import { QMETRY_PATHS } from "../config/rest-endpoints.js";
|
|
4
4
|
import { DEFAULT_IMPORT_AUTOMATION_PAYLOAD } from "../types/automation.js";
|
|
@@ -91,7 +91,7 @@ async function importAutomationResults(token, baseUrl, project, payload) {
|
|
|
91
91
|
const headers = {
|
|
92
92
|
apikey: token,
|
|
93
93
|
project: finalPayload.projectID || project || QMETRY_DEFAULTS.PROJECT_KEY,
|
|
94
|
-
"User-Agent":
|
|
94
|
+
"User-Agent": getUserAgent(),
|
|
95
95
|
"qmetry-source": "smartbear-mcp"
|
|
96
96
|
// Note: Content-Type will be set automatically by fetch for FormData
|
|
97
97
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getUserAgent } from "../../common/info.js";
|
|
2
2
|
import { CONTENT_TYPES, HTTP_HEADERS } from "../config/constants.js";
|
|
3
3
|
class AuthService {
|
|
4
4
|
apiKey;
|
|
@@ -13,7 +13,7 @@ class AuthService {
|
|
|
13
13
|
return {
|
|
14
14
|
[HTTP_HEADERS.API_KEY]: this.apiKey,
|
|
15
15
|
[HTTP_HEADERS.CONTENT_TYPE]: CONTENT_TYPES.JSON,
|
|
16
|
-
[HTTP_HEADERS.USER_AGENT]:
|
|
16
|
+
[HTTP_HEADERS.USER_AGENT]: getUserAgent(),
|
|
17
17
|
[HTTP_HEADERS.ACCEPT]: CONTENT_TYPES.JSON
|
|
18
18
|
};
|
|
19
19
|
}
|
package/dist/reflect/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { getUserAgent } from "../common/info.js";
|
|
3
3
|
import { getRequestHeader } from "../common/request-context.js";
|
|
4
4
|
import { ToolError } from "../common/tools.js";
|
|
5
5
|
import { API_KEY_HEADER, REFLECT_API_TOKEN_HEADER, AUTHORIZATION_HEADER } from "./config/constants.js";
|
|
@@ -73,7 +73,7 @@ class ReflectClient {
|
|
|
73
73
|
return {
|
|
74
74
|
...this.getAuthHeader(),
|
|
75
75
|
"Content-Type": "application/json",
|
|
76
|
-
"User-Agent":
|
|
76
|
+
"User-Agent": getUserAgent()
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
getSessionState(sessionId) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appendClientIdentity } from "../../common/info.js";
|
|
1
2
|
import { ToolError } from "../../common/tools.js";
|
|
2
3
|
import { buildSubdomainCandidate, buildSuffixedSubdomain, buildPortalName, isConflictError, isOrganizationPortalConflict } from "./portal-utils.js";
|
|
3
4
|
import { findTableOfContentsItem, buildPortalLiveUrl, normalizeSlug } from "./utils.js";
|
|
@@ -22,7 +23,7 @@ class SwaggerAPI {
|
|
|
22
23
|
this.userAgent = userAgent;
|
|
23
24
|
}
|
|
24
25
|
get headers() {
|
|
25
|
-
return this.config.getHeaders(this.userAgent);
|
|
26
|
+
return this.config.getHeaders(appendClientIdentity(this.userAgent));
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
29
|
* Core response parsing logic shared between different response handlers.
|
|
@@ -677,7 +678,8 @@ class SwaggerAPI {
|
|
|
677
678
|
contentType = "markdown",
|
|
678
679
|
source = "internal",
|
|
679
680
|
order = 0,
|
|
680
|
-
parentId = null
|
|
681
|
+
parentId = null,
|
|
682
|
+
pageSlug
|
|
681
683
|
} = args;
|
|
682
684
|
if (contentType === "html" && source === "internal" && pageContent !== void 0) {
|
|
683
685
|
throw new ToolError(
|
|
@@ -694,12 +696,12 @@ class SwaggerAPI {
|
|
|
694
696
|
);
|
|
695
697
|
}
|
|
696
698
|
const section = sections.items[0];
|
|
697
|
-
const
|
|
699
|
+
const resolvedPageSlug = pageSlug || normalizeSlug(pageTitle);
|
|
698
700
|
const normalizedTitle = pageTitle.slice(0, 255);
|
|
699
701
|
const tocItem = await this.createTableOfContents(section.id, {
|
|
700
702
|
type: "new",
|
|
701
703
|
title: normalizedTitle,
|
|
702
|
-
slug:
|
|
704
|
+
slug: resolvedPageSlug,
|
|
703
705
|
order,
|
|
704
706
|
parentId,
|
|
705
707
|
content: {
|
|
@@ -724,7 +726,7 @@ class SwaggerAPI {
|
|
|
724
726
|
sectionSlug: section.slug,
|
|
725
727
|
pageDetails: {
|
|
726
728
|
tableOfContentsId: tocItem.id,
|
|
727
|
-
slug:
|
|
729
|
+
slug: resolvedPageSlug,
|
|
728
730
|
title: normalizedTitle,
|
|
729
731
|
content: {
|
|
730
732
|
type: contentType,
|