@smartbear/mcp 0.25.0 → 0.26.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 +12 -3
- package/dist/bearq/client.js +15 -14
- package/dist/bugsnag/client.js +46 -25
- package/dist/collaborator/client.js +21 -15
- package/dist/common/client-registry.js +20 -46
- package/dist/common/info.js +5 -1
- package/dist/common/request-context.js +20 -0
- package/dist/common/server.js +1 -14
- package/dist/common/transport-http.js +63 -80
- package/dist/common/transport-stdio.js +26 -29
- package/dist/package.json.js +1 -1
- package/dist/pactflow/client.js +45 -32
- package/dist/qmetry/client/api/client-api.js +2 -2
- package/dist/qmetry/client/automation.js +2 -2
- package/dist/qmetry/client.js +18 -20
- package/dist/qtm4j/client.js +31 -17
- package/dist/qtm4j/http/auth-service.js +2 -2
- package/dist/reflect/client.js +28 -17
- package/dist/reflect/config/constants.js +2 -0
- package/dist/reflect/tool/tests/get-test-detail.js +37 -0
- package/dist/swagger/client/functional-testing-api.js +36 -0
- package/dist/swagger/client/functional-testing-tools.js +11 -0
- package/dist/swagger/client/tools.js +3 -1
- package/dist/swagger/client.js +66 -27
- package/dist/zephyr/client.js +21 -14
- package/dist/zephyr/common/auth-service.js +2 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -35,6 +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
39
|
- [QMetry](https://developer.smartbear.com/smartbear-mcp/docs/qmetry-integration) - QMetry Test Management capabilities
|
|
39
40
|
- [Zephyr](https://developer.smartbear.com/smartbear-mcp/docs/zephyr-integration) - Zephyr Test Management capabilities
|
|
40
41
|
- [Collaborator](https://developer.smartbear.com/smartbear-mcp/docs/collaborator-integration) - Review and Remote System Configuration management capabilities
|
|
@@ -52,7 +53,7 @@ For BugSnag, Swagger, and Zephyr, SmartBear hosts Remote MCP Servers that you ca
|
|
|
52
53
|
|
|
53
54
|
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.
|
|
54
55
|
|
|
55
|
-
> **Need BearQ, Reflect, QMetry, QTM4J, PactFlow, or
|
|
56
|
+
> **Need BearQ, Reflect, QMetry, QTM4J, PactFlow, Collaborator, or Functional Testing?** These products are only available via the local npm package below, which bundles all products into a single MCP server.
|
|
56
57
|
|
|
57
58
|
## Prerequisites
|
|
58
59
|
|
|
@@ -108,7 +109,8 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
|
|
|
108
109
|
"COLLABORATOR_LOGIN_TICKET": "${input:collab_login_ticket}",
|
|
109
110
|
"QTM4J_API_KEY": "${input:qtm4j_api_key}",
|
|
110
111
|
"QTM4J_BASE_URL": "${input:qtm4j_base_url}",
|
|
111
|
-
"QTM4J_AUTOMATION_API_KEY": "${input:qtm4j_automation_api_key}"
|
|
112
|
+
"QTM4J_AUTOMATION_API_KEY": "${input:qtm4j_automation_api_key}",
|
|
113
|
+
"SWAGGER_FUNCTIONAL_TESTING_API_TOKEN": "${input:swagger_functional_testing_api_token}"
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
},
|
|
@@ -250,6 +252,12 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
|
|
|
250
252
|
"type": "promptString",
|
|
251
253
|
"description": "QTM4J Automation API Key - required for automation tools, leave blank to disable them",
|
|
252
254
|
"password": true
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"id": "swagger_functional_testing_api_token",
|
|
258
|
+
"type": "promptString",
|
|
259
|
+
"description": "Swagger Functional Testing API Token - leave blank to disable Functional Testing tools",
|
|
260
|
+
"password": true
|
|
253
261
|
}
|
|
254
262
|
]
|
|
255
263
|
}
|
|
@@ -291,7 +299,8 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
|
|
|
291
299
|
"COLLABORATOR_LOGIN_TICKET": "your collab login ticket",
|
|
292
300
|
"QTM4J_API_KEY": "your_qtm4j_key",
|
|
293
301
|
"QTM4J_BASE_URL": "https://qtmcloud.qmetry.com",
|
|
294
|
-
"QTM4J_AUTOMATION_API_KEY": "your_qtm4j_automation_api_key"
|
|
302
|
+
"QTM4J_AUTOMATION_API_KEY": "your_qtm4j_automation_api_key",
|
|
303
|
+
"SWAGGER_FUNCTIONAL_TESTING_API_TOKEN": "your_swagger_functional_testing_api_token"
|
|
295
304
|
}
|
|
296
305
|
}
|
|
297
306
|
}
|
package/dist/bearq/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { USER_AGENT } from "../common/info.js";
|
|
3
|
+
import { getRequestHeader } from "../common/request-context.js";
|
|
3
4
|
import { ToolError } from "../common/tools.js";
|
|
4
5
|
import { DEFAULT_API_BASE_URL, AUTHORIZATION_HEADER } from "./config/constants.js";
|
|
5
6
|
import { ChatWithQaLead } from "./tool/tasks/chat-with-qa-lead.js";
|
|
@@ -15,33 +16,33 @@ import { RunTestsInFunctionalAreas } from "./tool/tasks/run-tests-in-functional-
|
|
|
15
16
|
import { StopTask } from "./tool/tasks/stop-task.js";
|
|
16
17
|
import { WaitForTask } from "./tool/tasks/wait-for-task.js";
|
|
17
18
|
const ConfigurationSchema = z.object({
|
|
19
|
+
api_token: z.string().describe("BearQ workspace API token (Bearer)."),
|
|
18
20
|
api_base_url: z.string().optional().describe(
|
|
19
21
|
"Override the BearQ public API base URL. Defaults to https://api.bearq.smartbear.com"
|
|
20
22
|
)
|
|
21
23
|
});
|
|
22
|
-
const AuthenticationSchema = z.object({
|
|
23
|
-
api_token: z.string().describe("BearQ workspace API token (Bearer).")
|
|
24
|
-
});
|
|
25
24
|
class BearQClient {
|
|
26
|
-
|
|
25
|
+
_apiToken;
|
|
27
26
|
_baseUrl = DEFAULT_API_BASE_URL;
|
|
28
27
|
name = "BearQ";
|
|
29
28
|
capabilityPrefix = "bearq";
|
|
30
29
|
configPrefix = "BearQ";
|
|
31
30
|
config = ConfigurationSchema;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this.server = server;
|
|
31
|
+
async configure(_server, config) {
|
|
32
|
+
this._apiToken = config.api_token;
|
|
35
33
|
if (config.api_base_url) this._baseUrl = config.api_base_url;
|
|
36
34
|
}
|
|
37
35
|
getAuthToken() {
|
|
38
|
-
|
|
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;
|
|
39
43
|
}
|
|
40
44
|
isConfigured() {
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
hasAuth() {
|
|
44
|
-
return this.isConfigured() && !!this.getAuthToken();
|
|
45
|
+
return true;
|
|
45
46
|
}
|
|
46
47
|
getBaseUrl() {
|
|
47
48
|
return this._baseUrl;
|
|
@@ -52,7 +53,7 @@ class BearQClient {
|
|
|
52
53
|
return {
|
|
53
54
|
Authorization: `Bearer ${token}`,
|
|
54
55
|
"Content-Type": "application/json",
|
|
55
|
-
"User-Agent":
|
|
56
|
+
"User-Agent": USER_AGENT
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
async registerTools(register, _getInput) {
|
package/dist/bugsnag/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { USER_AGENT } from "../common/info.js";
|
|
3
|
+
import { getRequestHeader } from "../common/request-context.js";
|
|
3
4
|
import { ToolError } from "../common/tools.js";
|
|
4
5
|
import { CurrentUserAPI } from "./client/api/CurrentUser.js";
|
|
5
6
|
import { Configuration } from "./client/api/configuration.js";
|
|
@@ -39,14 +40,11 @@ const EXCLUDED_EVENT_FIELDS = /* @__PURE__ */ new Set([
|
|
|
39
40
|
// This is searches multiple fields and is more a convenience for humans, we're removing to avoid over-matching
|
|
40
41
|
]);
|
|
41
42
|
const ConfigurationSchema = z.object({
|
|
43
|
+
auth_token: z.string().describe("BugSnag personal access token"),
|
|
42
44
|
project_api_key: z.string().describe("BugSnag project API key").optional(),
|
|
43
|
-
endpoint: z.url().describe("BugSnag endpoint URL").optional()
|
|
44
|
-
});
|
|
45
|
-
const AuthenticationSchema = z.object({
|
|
46
|
-
auth_token: z.string().describe("BugSnag personal access token").optional()
|
|
45
|
+
endpoint: z.string().url().describe("BugSnag endpoint URL").optional()
|
|
47
46
|
});
|
|
48
47
|
class BugsnagClient {
|
|
49
|
-
server;
|
|
50
48
|
cache;
|
|
51
49
|
_projectApiKey;
|
|
52
50
|
_isConfigured = false;
|
|
@@ -54,7 +52,7 @@ class BugsnagClient {
|
|
|
54
52
|
_errorsApi;
|
|
55
53
|
_projectApi;
|
|
56
54
|
_appEndpoint;
|
|
57
|
-
|
|
55
|
+
_authToken;
|
|
58
56
|
get currentUserApi() {
|
|
59
57
|
if (!this._currentUserApi) throw new Error("Client not configured");
|
|
60
58
|
return this._currentUserApi;
|
|
@@ -76,9 +74,7 @@ class BugsnagClient {
|
|
|
76
74
|
configPrefix = "Bugsnag";
|
|
77
75
|
config = ConfigurationSchema;
|
|
78
76
|
defaultToolsets = ["Projects"];
|
|
79
|
-
authenticationFields = AuthenticationSchema;
|
|
80
77
|
async configure(server, config) {
|
|
81
|
-
this.server = server;
|
|
82
78
|
this.cache = server.getCache();
|
|
83
79
|
this._appEndpoint = this.getEndpoint(
|
|
84
80
|
"app",
|
|
@@ -86,20 +82,48 @@ class BugsnagClient {
|
|
|
86
82
|
config.endpoint
|
|
87
83
|
);
|
|
88
84
|
this._projectApiKey = config.project_api_key;
|
|
89
|
-
this.
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
this._authToken = config.auth_token;
|
|
86
|
+
await this.initializeApis(config);
|
|
87
|
+
}
|
|
88
|
+
getAuthToken() {
|
|
89
|
+
const contextHeader = getRequestHeader("Bugsnag-Auth-Token");
|
|
90
|
+
if (contextHeader) {
|
|
91
|
+
let token = Array.isArray(contextHeader) ? contextHeader[0] : contextHeader;
|
|
92
|
+
if (token.startsWith("token ")) {
|
|
93
|
+
token = token.substring(6);
|
|
94
|
+
}
|
|
95
|
+
return `token ${token}`;
|
|
96
|
+
}
|
|
97
|
+
const bearerToken = this.getBearerToken();
|
|
98
|
+
if (bearerToken) {
|
|
99
|
+
return bearerToken;
|
|
100
|
+
}
|
|
101
|
+
return this._authToken ? `token ${this._authToken}` : null;
|
|
102
|
+
}
|
|
103
|
+
getBearerToken() {
|
|
104
|
+
const contextHeader = getRequestHeader("Authorization");
|
|
105
|
+
if (contextHeader) {
|
|
106
|
+
let token = Array.isArray(contextHeader) ? contextHeader[0] : contextHeader;
|
|
107
|
+
if (token.startsWith("Bearer ")) {
|
|
108
|
+
token = token.substring(7);
|
|
109
|
+
}
|
|
110
|
+
return `Bearer ${token}`;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
async initializeApis(config) {
|
|
115
|
+
const apiConfig = new Configuration({
|
|
116
|
+
apiKey: (_name) => {
|
|
117
|
+
const authToken = this.getAuthToken();
|
|
92
118
|
if (authToken) {
|
|
93
|
-
return
|
|
119
|
+
return authToken;
|
|
94
120
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
return void 0;
|
|
121
|
+
throw new Error(
|
|
122
|
+
"Authentication token not found in request headers or configuration"
|
|
123
|
+
);
|
|
100
124
|
},
|
|
101
125
|
headers: {
|
|
102
|
-
"User-Agent":
|
|
126
|
+
"User-Agent": USER_AGENT,
|
|
103
127
|
"Content-Type": "application/json",
|
|
104
128
|
"X-Bugsnag-API": "true",
|
|
105
129
|
"X-Version": "2"
|
|
@@ -110,17 +134,14 @@ class BugsnagClient {
|
|
|
110
134
|
config.endpoint
|
|
111
135
|
)
|
|
112
136
|
});
|
|
113
|
-
this._currentUserApi = new CurrentUserAPI(
|
|
114
|
-
this._errorsApi = new ErrorAPI(
|
|
115
|
-
this._projectApi = new ProjectAPI(
|
|
137
|
+
this._currentUserApi = new CurrentUserAPI(apiConfig);
|
|
138
|
+
this._errorsApi = new ErrorAPI(apiConfig);
|
|
139
|
+
this._projectApi = new ProjectAPI(apiConfig);
|
|
116
140
|
this._isConfigured = true;
|
|
117
141
|
}
|
|
118
142
|
isConfigured() {
|
|
119
143
|
return this._isConfigured;
|
|
120
144
|
}
|
|
121
|
-
hasAuth() {
|
|
122
|
-
return this.isConfigured() && !!this.apiConfig?.apiKey();
|
|
123
|
-
}
|
|
124
145
|
// If the endpoint is not provided, it will use the default API endpoint based on the project API key.
|
|
125
146
|
// if the project api key is not provided, the endpoint will be the default API endpoint.
|
|
126
147
|
// if the endpoint is provided, it will be used as is for custom domains, or normalized for known domains.
|
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { USER_AGENT } from "../common/info.js";
|
|
3
|
+
import { getRequestHeader } from "../common/request-context.js";
|
|
2
4
|
const ConfigurationSchema = z.object({
|
|
3
|
-
base_url: z.url().describe("Collaborator server base URL")
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
login: z.string().describe("Collaborator username for authentication").optional(),
|
|
7
|
-
ticket: z.string().describe("Collaborator login ticket for authentication").optional()
|
|
5
|
+
base_url: z.url().describe("Collaborator server base URL"),
|
|
6
|
+
username: z.string().describe("Collaborator username for authentication").optional(),
|
|
7
|
+
login_ticket: z.string().describe("Collaborator login ticket for authentication").optional()
|
|
8
8
|
});
|
|
9
9
|
class CollaboratorClient {
|
|
10
10
|
name = "Collaborator";
|
|
11
11
|
capabilityPrefix = "collaborator";
|
|
12
12
|
configPrefix = "Collaborator";
|
|
13
13
|
config = ConfigurationSchema;
|
|
14
|
-
authenticationFields = AuthenticationSchema;
|
|
15
14
|
baseUrl;
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
username;
|
|
16
|
+
loginTicket;
|
|
17
|
+
async configure(_server, config, _cache) {
|
|
18
18
|
this.baseUrl = config.base_url;
|
|
19
|
-
this.
|
|
19
|
+
this.username = config.username;
|
|
20
|
+
this.loginTicket = config.login_ticket;
|
|
20
21
|
}
|
|
21
22
|
isConfigured() {
|
|
22
23
|
return this.baseUrl !== void 0;
|
|
23
24
|
}
|
|
24
|
-
hasAuth() {
|
|
25
|
-
return this.isConfigured() && this.server?.getEnv("Login", this) !== void 0 && this.server?.getEnv("Ticket", this) !== void 0;
|
|
26
|
-
}
|
|
27
25
|
/**
|
|
28
26
|
* Calls the Collaborator API with the given commands, prepending authentication automatically.
|
|
29
27
|
* @param commands Array of Collaborator API commands (excluding authentication)
|
|
@@ -31,8 +29,16 @@ class CollaboratorClient {
|
|
|
31
29
|
*/
|
|
32
30
|
async call(commands) {
|
|
33
31
|
const url = `${this.baseUrl}/services/json/v1`;
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
let login = this.username;
|
|
33
|
+
let ticket = this.loginTicket;
|
|
34
|
+
const contextLogin = getRequestHeader("Collaborator-Login");
|
|
35
|
+
const contextTicket = getRequestHeader("Collaborator-Ticket");
|
|
36
|
+
if (contextLogin) {
|
|
37
|
+
login = Array.isArray(contextLogin) ? contextLogin[0] : contextLogin;
|
|
38
|
+
}
|
|
39
|
+
if (contextTicket) {
|
|
40
|
+
ticket = Array.isArray(contextTicket) ? contextTicket[0] : contextTicket;
|
|
41
|
+
}
|
|
36
42
|
const body = [
|
|
37
43
|
{
|
|
38
44
|
command: "SessionService.authenticate",
|
|
@@ -42,7 +48,7 @@ class CollaboratorClient {
|
|
|
42
48
|
];
|
|
43
49
|
const response = await fetch(url, {
|
|
44
50
|
method: "POST",
|
|
45
|
-
headers: { "Content-Type": "application/json" },
|
|
51
|
+
headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT },
|
|
46
52
|
body: JSON.stringify(body)
|
|
47
53
|
});
|
|
48
54
|
if (!response.ok) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ZodURL
|
|
1
|
+
import { ZodURL } from "zod";
|
|
2
2
|
import { fullyUnwrapZodType, isOptionalType } from "./zod-utils.js";
|
|
3
3
|
class ClientRegistry {
|
|
4
4
|
entries = [];
|
|
@@ -84,57 +84,31 @@ class ClientRegistry {
|
|
|
84
84
|
return this.entries.filter((entry) => this.isClientEnabled(entry.name));
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
87
|
+
* Configures all enabled clients on the given MCP server
|
|
88
88
|
* @param server The MCP server on which the client is registered
|
|
89
89
|
* @param getConfigValue A function that obtains a configuration value for the given client and requirement name
|
|
90
|
-
* @returns The number of clients successfully
|
|
90
|
+
* @returns The number of clients successfully configured
|
|
91
91
|
*/
|
|
92
|
-
async
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const value = getConfigValue(configKey, client);
|
|
104
|
-
if (value) {
|
|
105
|
-
this.validateAllowedEndpoint(client.config.shape[configKey], value);
|
|
106
|
-
config[configKey] = value;
|
|
107
|
-
} else if (!isOptionalType(client.config.shape[configKey])) {
|
|
108
|
-
continue clientLoop;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
let parsedConfig;
|
|
112
|
-
try {
|
|
113
|
-
parsedConfig = client.config.parse(config);
|
|
114
|
-
} catch (error) {
|
|
115
|
-
if (error instanceof ZodError) {
|
|
116
|
-
console.warn(
|
|
117
|
-
`Configuration for client ${client.name} is invalid: ${error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ")}`
|
|
118
|
-
);
|
|
119
|
-
} else {
|
|
120
|
-
console.warn(
|
|
121
|
-
`Unable to apply configuration for client ${client.name}: ${error}`
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
await client.configure(server, parsedConfig);
|
|
127
|
-
if (!client.isConfigured()) {
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
if (authorizationCheck && !client.hasAuth()) {
|
|
131
|
-
continue;
|
|
92
|
+
async configure(server, getConfigValue, ignoreMissingRequiredConfigs = false) {
|
|
93
|
+
let configuredCount = 0;
|
|
94
|
+
entryLoop: for (const entry of this.getAll()) {
|
|
95
|
+
const config = {};
|
|
96
|
+
for (const configKey of Object.keys(entry.config.shape)) {
|
|
97
|
+
const value = getConfigValue(entry, configKey);
|
|
98
|
+
if (value !== null) {
|
|
99
|
+
this.validateAllowedEndpoint(entry.config.shape[configKey], value);
|
|
100
|
+
config[configKey] = value;
|
|
101
|
+
} else if (!ignoreMissingRequiredConfigs && !isOptionalType(entry.config.shape[configKey])) {
|
|
102
|
+
continue entryLoop;
|
|
132
103
|
}
|
|
133
104
|
}
|
|
134
|
-
await
|
|
135
|
-
|
|
105
|
+
await entry.configure(server, config);
|
|
106
|
+
if (entry.isConfigured()) {
|
|
107
|
+
await server.addClient(entry);
|
|
108
|
+
configuredCount++;
|
|
109
|
+
}
|
|
136
110
|
}
|
|
137
|
-
return
|
|
111
|
+
return configuredCount;
|
|
138
112
|
}
|
|
139
113
|
/**
|
|
140
114
|
* Clear all registrations (useful for testing)
|
package/dist/common/info.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import packageJson from "../package.json.js";
|
|
2
2
|
const MCP_SERVER_NAME = packageJson.config.mcpServerName;
|
|
3
3
|
const MCP_SERVER_VERSION = packageJson.version;
|
|
4
|
+
const MCP_TRANSPORT = process.env.MCP_TRANSPORT?.toLowerCase().trim() || "stdio";
|
|
5
|
+
const USER_AGENT = `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION} ${MCP_TRANSPORT}`;
|
|
4
6
|
export {
|
|
5
7
|
MCP_SERVER_NAME,
|
|
6
|
-
MCP_SERVER_VERSION
|
|
8
|
+
MCP_SERVER_VERSION,
|
|
9
|
+
MCP_TRANSPORT,
|
|
10
|
+
USER_AGENT
|
|
7
11
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
const requestContextStorage = new AsyncLocalStorage();
|
|
3
|
+
function withRequestContext(req, fn) {
|
|
4
|
+
return requestContextStorage.run({ headers: req.headers }, fn);
|
|
5
|
+
}
|
|
6
|
+
function getRequestContext() {
|
|
7
|
+
return requestContextStorage.getStore();
|
|
8
|
+
}
|
|
9
|
+
function getRequestHeader(name) {
|
|
10
|
+
const context = getRequestContext();
|
|
11
|
+
if (!context?.headers) return void 0;
|
|
12
|
+
const headerValue = context.headers[name] || context.headers[name.toLowerCase()];
|
|
13
|
+
return headerValue;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
getRequestContext,
|
|
17
|
+
getRequestHeader,
|
|
18
|
+
requestContextStorage,
|
|
19
|
+
withRequestContext
|
|
20
|
+
};
|
package/dist/common/server.js
CHANGED
|
@@ -12,8 +12,7 @@ class SmartBearMcpServer extends McpServer {
|
|
|
12
12
|
elicitationSupported = false;
|
|
13
13
|
clients = [];
|
|
14
14
|
enabledToolsets;
|
|
15
|
-
|
|
16
|
-
constructor(getEnvFn, enabledToolsets) {
|
|
15
|
+
constructor(enabledToolsets) {
|
|
17
16
|
super(
|
|
18
17
|
{
|
|
19
18
|
name: MCP_SERVER_NAME,
|
|
@@ -29,7 +28,6 @@ class SmartBearMcpServer extends McpServer {
|
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
);
|
|
32
|
-
this.getEnvFn = getEnvFn;
|
|
33
31
|
this.cache = new CacheService();
|
|
34
32
|
if (enabledToolsets) {
|
|
35
33
|
this.enabledToolsets = enabledToolsets.split(",").map((s) => s.trim().toLowerCase());
|
|
@@ -38,17 +36,6 @@ class SmartBearMcpServer extends McpServer {
|
|
|
38
36
|
getCache() {
|
|
39
37
|
return this.cache;
|
|
40
38
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Makes the server's getEnv function available to clients, validating that it is defined in the client's authentication fields if a client is provided
|
|
43
|
-
*/
|
|
44
|
-
getEnv = (key, client) => {
|
|
45
|
-
if (client && !Object.keys(client.authenticationFields.shape).includes(key)) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
`Environment variable "${key}" is not defined in the ${client.name} client's authentication schema.`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
return this.getEnvFn(key, client);
|
|
51
|
-
};
|
|
52
39
|
setSamplingSupported(supported) {
|
|
53
40
|
this.samplingSupported = supported;
|
|
54
41
|
}
|