@smartbear/mcp 0.17.0 → 0.18.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.
@@ -1,11 +1,12 @@
1
1
  import zod__default from "zod";
2
+ import { getRequestHeader } from "../common/request-context.js";
2
3
  import { findAutoResolveConfig, autoResolveViewIdAndFolderPath } from "./client/auto-resolve.js";
3
4
  import { QMETRY_HANDLER_MAP } from "./client/handlers.js";
4
5
  import { getProjectInfo } from "./client/project.js";
5
6
  import { TOOLS } from "./client/tools/index.js";
6
7
  import { QMETRY_DEFAULTS } from "./config/constants.js";
7
8
  const ConfigurationSchema = zod__default.object({
8
- api_key: zod__default.string().describe("QMetry API key for authentication"),
9
+ api_key: zod__default.string().describe("QMetry API key for authentication").optional(),
9
10
  base_url: zod__default.string().url().optional().describe(
10
11
  "Optional QMetry base URL for custom or region-specific endpoints"
11
12
  )
@@ -25,9 +26,16 @@ class QmetryClient {
25
26
  }
26
27
  }
27
28
  isConfigured() {
28
- return this.token !== void 0;
29
+ return true;
29
30
  }
30
31
  getToken() {
32
+ let contextToken = getRequestHeader("Qmetry-Token") || getRequestHeader("apikey");
33
+ if (Array.isArray(contextToken)) {
34
+ contextToken = contextToken[0];
35
+ }
36
+ if (contextToken) {
37
+ return contextToken;
38
+ }
31
39
  if (!this.token) throw new Error("Client not configured");
32
40
  return this.token;
33
41
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
3
+ import { getRequestHeader } from "../common/request-context.js";
3
4
  import { ToolError } from "../common/tools.js";
4
5
  import { API_KEY_HEADER } from "./config/constants.js";
5
6
  import { SapTest } from "./prompt/sap-test.js";
@@ -18,11 +19,10 @@ import { ListSegments } from "./tool/tests/list-segments.js";
18
19
  import { ListTests } from "./tool/tests/list-tests.js";
19
20
  import { RunTest } from "./tool/tests/run-test.js";
20
21
  const ConfigurationSchema = z.object({
21
- api_token: z.string().describe("Reflect API authentication token")
22
+ api_token: z.string().describe("Reflect API authentication token").optional()
22
23
  });
23
24
  class ReflectClient {
24
- headers = {};
25
- apiToken = "";
25
+ _apiToken;
26
26
  activeConnections = /* @__PURE__ */ new Map();
27
27
  sessionStates = /* @__PURE__ */ new Map();
28
28
  mcpSessionConnections = /* @__PURE__ */ new Map();
@@ -31,21 +31,35 @@ class ReflectClient {
31
31
  configPrefix = "Reflect";
32
32
  config = ConfigurationSchema;
33
33
  async configure(_server, config, _cache) {
34
- this.apiToken = config.api_token;
35
- this.headers = {
36
- [API_KEY_HEADER]: `${config.api_token}`,
37
- "Content-Type": "application/json",
38
- "User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
39
- };
34
+ this._apiToken = config.api_token;
35
+ }
36
+ getAuthToken() {
37
+ const contextHeader = getRequestHeader("Reflect-Api-Token") || getRequestHeader("X-API-KEY") || getRequestHeader("Authorization");
38
+ if (contextHeader) {
39
+ let token = Array.isArray(contextHeader) ? contextHeader[0] : contextHeader;
40
+ if (token.startsWith("Bearer ")) {
41
+ token = token.substring(7);
42
+ }
43
+ return token;
44
+ }
45
+ return this._apiToken || null;
40
46
  }
41
47
  isConfigured() {
42
- return Object.keys(this.headers).length !== 0;
48
+ return true;
43
49
  }
44
50
  getApiToken() {
45
- return this.apiToken;
51
+ return this.getAuthToken() || "";
46
52
  }
47
53
  getHeaders() {
48
- return this.headers;
54
+ const token = this.getAuthToken();
55
+ if (!token) {
56
+ throw new Error("Reflect API token not found");
57
+ }
58
+ return {
59
+ [API_KEY_HEADER]: token,
60
+ "Content-Type": "application/json",
61
+ "User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
62
+ };
49
63
  }
50
64
  getSessionState(sessionId) {
51
65
  return this.sessionStates.get(sessionId);
@@ -30,6 +30,7 @@ class ConnectToSession extends Tool {
30
30
  if (!sessionId) throw new ToolError("sessionId argument is required");
31
31
  if (this.client.isSessionConnected(sessionId)) {
32
32
  const state = this.client.getSessionState(sessionId);
33
+ if (!state) throw new ToolError("Failed to get session state");
33
34
  const { platform: platform2, test: test2 } = state;
34
35
  return {
35
36
  content: [
@@ -11,10 +11,13 @@ function hasErrorsFound(value) {
11
11
  }
12
12
  class SwaggerAPI {
13
13
  config;
14
- headers;
14
+ userAgent;
15
15
  constructor(config, userAgent) {
16
16
  this.config = config;
17
- this.headers = config.getHeaders(userAgent);
17
+ this.userAgent = userAgent;
18
+ }
19
+ get headers() {
20
+ return this.config.getHeaders(this.userAgent);
18
21
  }
19
22
  /**
20
23
  * Core response parsing logic shared between different response handlers.
@@ -1,28 +1,36 @@
1
1
  class SwaggerConfiguration {
2
- token;
2
+ tokenProvider;
3
3
  portalBasePath;
4
4
  registryBasePath;
5
5
  uiBasePath;
6
6
  userManagementBasePath;
7
- headers;
7
+ defaultHeaders;
8
8
  constructor(param) {
9
- this.token = param.token;
9
+ if (typeof param.token === "string") {
10
+ this.tokenProvider = () => param.token;
11
+ } else {
12
+ this.tokenProvider = param.token;
13
+ }
10
14
  this.portalBasePath = param.portalBasePath || "https://api.portal.swaggerhub.com/v1";
11
15
  this.registryBasePath = param.registryBasePath || "https://api.swaggerhub.com";
12
16
  this.uiBasePath = param.uiBasePath || "https://app.swaggerhub.com";
13
17
  this.userManagementBasePath = param.userManagementBasePath || `${this.registryBasePath}/user-management/v1`;
14
- this.headers = {
15
- Authorization: `Bearer ${this.token}`,
18
+ this.defaultHeaders = {
16
19
  "Content-Type": "application/json",
17
20
  ...param.headers
18
21
  };
19
22
  }
20
23
  /**
21
- * Get headers with User-Agent included
24
+ * Get headers with User-Agent included and dynamic Auth token
22
25
  */
23
26
  getHeaders(userAgent) {
27
+ const token = this.tokenProvider();
28
+ if (!token) {
29
+ throw new Error("Swagger API token not found");
30
+ }
24
31
  return {
25
- ...this.headers,
32
+ Authorization: `Bearer ${token}`,
33
+ ...this.defaultHeaders,
26
34
  "User-Agent": userAgent
27
35
  };
28
36
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
3
+ import { getRequestHeader } from "../common/request-context.js";
3
4
  import "./config-utils.js";
4
5
  import { SwaggerAPI } from "./client/api.js";
5
6
  import { SwaggerConfiguration } from "./client/configuration.js";
@@ -8,21 +9,23 @@ import "./client/registry-types.js";
8
9
  import { TOOLS } from "./client/tools.js";
9
10
  import "./client/user-management-types.js";
10
11
  const ConfigurationSchema = z.object({
11
- api_key: z.string().describe("Swagger API key for authentication"),
12
+ api_key: z.string().describe("Swagger API key for authentication").optional(),
12
13
  portal_base_path: z.string().optional().describe("Base path for Portal API requests (optional)"),
13
14
  registry_base_path: z.string().optional().describe("Base path for Registry API requests (optional)"),
14
15
  ui_base_path: z.string().optional().describe("Base URL for the SwaggerHub UI (optional)")
15
16
  });
16
17
  class SwaggerClient {
17
18
  api;
19
+ _apiKey;
18
20
  name = "Swagger";
19
21
  toolPrefix = "swagger";
20
22
  configPrefix = "Swagger";
21
23
  config = ConfigurationSchema;
22
24
  async configure(_server, config, _cache) {
25
+ this._apiKey = config.api_key;
23
26
  this.api = new SwaggerAPI(
24
27
  new SwaggerConfiguration({
25
- token: config.api_key,
28
+ token: () => this.getAuthToken(),
26
29
  portalBasePath: config.portal_base_path,
27
30
  registryBasePath: config.registry_base_path,
28
31
  uiBasePath: config.ui_base_path
@@ -30,6 +33,17 @@ class SwaggerClient {
30
33
  `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
31
34
  );
32
35
  }
36
+ getAuthToken() {
37
+ const contextHeader = getRequestHeader("Swagger-Api-Key") || getRequestHeader("Authorization");
38
+ if (contextHeader) {
39
+ let token = Array.isArray(contextHeader) ? contextHeader[0] : contextHeader;
40
+ if (token.startsWith("Bearer ")) {
41
+ token = token.substring(7);
42
+ }
43
+ return token;
44
+ }
45
+ return this._apiKey || null;
46
+ }
33
47
  isConfigured() {
34
48
  return this.api !== void 0;
35
49
  }
@@ -1,4 +1,5 @@
1
1
  import zod__default from "zod";
2
+ import { getRequestHeader } from "../common/request-context.js";
2
3
  import { ApiClient } from "./common/api-client.js";
3
4
  import { GetEnvironments } from "./tool/environment/get-environments.js";
4
5
  import { CreateFolder } from "./tool/folder/create-folder.js";
@@ -37,21 +38,34 @@ import { UpdateTestExecution } from "./tool/test-execution/update-test-execution
37
38
  import { UpdateTestExecutionSteps } from "./tool/test-execution/update-test-steps.js";
38
39
  const BASE_URL_DEFAULT = "https://api.zephyrscale.smartbear.com/v2";
39
40
  const ConfigurationSchema = zod__default.object({
40
- api_token: zod__default.string().describe("Zephyr Scale API token for authentication"),
41
+ api_token: zod__default.string().describe("Zephyr Scale API token for authentication").optional(),
41
42
  base_url: zod__default.string().url().optional().describe("Zephyr Scale API base URL").default(BASE_URL_DEFAULT)
42
43
  });
43
44
  class ZephyrClient {
44
45
  apiClient;
46
+ _apiToken;
45
47
  name = "Zephyr";
46
48
  toolPrefix = "zephyr";
47
49
  configPrefix = "Zephyr";
48
50
  config = ConfigurationSchema;
49
51
  async configure(_server, config, _cache) {
52
+ this._apiToken = config.api_token;
50
53
  this.apiClient = new ApiClient(
51
- config.api_token,
54
+ () => this.getAuthToken(),
52
55
  config.base_url || BASE_URL_DEFAULT
53
56
  );
54
57
  }
58
+ getAuthToken() {
59
+ const contextHeader = getRequestHeader("Zephyr-Api-Token") || getRequestHeader("Authorization");
60
+ if (contextHeader) {
61
+ let token = Array.isArray(contextHeader) ? contextHeader[0] : contextHeader;
62
+ if (token.startsWith("Bearer ")) {
63
+ token = token.substring(7);
64
+ }
65
+ return token;
66
+ }
67
+ return this._apiToken || null;
68
+ }
55
69
  isConfigured() {
56
70
  return this.apiClient !== void 0;
57
71
  }
@@ -2,10 +2,24 @@ import { ToolError } from "../../common/tools.js";
2
2
  import { AuthService } from "./auth-service.js";
3
3
  class ApiClient {
4
4
  baseUrl;
5
- defaultHeaders;
6
- constructor(bearerToken, baseUrl) {
5
+ tokenProvider;
6
+ constructor(tokenOrProvider, baseUrl) {
7
7
  this.baseUrl = baseUrl.trim().replace(/\/$/, "");
8
- this.defaultHeaders = new AuthService(bearerToken).getAuthHeaders();
8
+ if (typeof tokenOrProvider === "string") {
9
+ this.tokenProvider = () => tokenOrProvider;
10
+ } else {
11
+ this.tokenProvider = tokenOrProvider;
12
+ }
13
+ }
14
+ get defaultHeaders() {
15
+ return this.getHeaders();
16
+ }
17
+ getHeaders() {
18
+ const token = this.tokenProvider();
19
+ if (!token) {
20
+ throw new ToolError("Zephyr API token not found");
21
+ }
22
+ return new AuthService(token).getAuthHeaders();
9
23
  }
10
24
  getUrl(endpoint, params) {
11
25
  const url = new URL(this.baseUrl + endpoint);
@@ -21,7 +35,7 @@ class ApiClient {
21
35
  async get(endpoint, params) {
22
36
  const response = await fetch(this.getUrl(endpoint, params), {
23
37
  method: "GET",
24
- headers: this.defaultHeaders
38
+ headers: this.getHeaders()
25
39
  });
26
40
  return await this.validateAndGetResponseBody(response);
27
41
  }
@@ -29,7 +43,7 @@ class ApiClient {
29
43
  const response = await fetch(this.getUrl(endpoint), {
30
44
  method: "POST",
31
45
  headers: {
32
- ...this.defaultHeaders,
46
+ ...this.getHeaders(),
33
47
  "Content-Type": "application/json"
34
48
  },
35
49
  body: JSON.stringify(body)
@@ -40,7 +54,7 @@ class ApiClient {
40
54
  const response = await fetch(this.getUrl(endpoint), {
41
55
  method: "PUT",
42
56
  headers: {
43
- ...this.defaultHeaders,
57
+ ...this.getHeaders(),
44
58
  "Content-Type": "application/json"
45
59
  },
46
60
  body: JSON.stringify(body)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartbear/mcp",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "MCP server for interacting SmartBear Products",
5
5
  "keywords": [
6
6
  "smartbear",