@smartbear/mcp 0.18.3 → 0.19.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,4 +1,4 @@
1
- const version = "0.18.3";
1
+ const version = "0.19.0";
2
2
  const config = { "mcpServerName": "SmartBear MCP Server" };
3
3
  const packageJson = {
4
4
  version,
@@ -8,7 +8,8 @@ const GenerationLanguages = [
8
8
  "dotnet",
9
9
  "kotlin",
10
10
  "swift",
11
- "php"
11
+ "php",
12
+ "python"
12
13
  ];
13
14
  const HttpMethods = [
14
15
  "GET",
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
3
3
  import { getRequestHeader } from "../common/request-context.js";
4
4
  import { ToolError } from "../common/tools.js";
5
- import { API_KEY_HEADER } from "./config/constants.js";
5
+ import { API_KEY_HEADER, REFLECT_API_TOKEN_HEADER, AUTHORIZATION_HEADER } from "./config/constants.js";
6
6
  import { SapTest } from "./prompt/sap-test.js";
7
7
  import { AddPromptStep } from "./tool/recording/add-prompt-step.js";
8
8
  import { AddSegment } from "./tool/recording/add-segment.js";
@@ -34,7 +34,7 @@ class ReflectClient {
34
34
  this._apiToken = config.api_token;
35
35
  }
36
36
  getAuthToken() {
37
- const contextHeader = getRequestHeader("Reflect-Api-Token") || getRequestHeader("X-API-KEY") || getRequestHeader("Authorization");
37
+ const contextHeader = getRequestHeader(REFLECT_API_TOKEN_HEADER) || getRequestHeader(API_KEY_HEADER) || getRequestHeader(AUTHORIZATION_HEADER);
38
38
  if (contextHeader) {
39
39
  let token = Array.isArray(contextHeader) ? contextHeader[0] : contextHeader;
40
40
  if (token.startsWith("Bearer ")) {
@@ -47,16 +47,30 @@ class ReflectClient {
47
47
  isConfigured() {
48
48
  return true;
49
49
  }
50
- getApiToken() {
51
- return this.getAuthToken() || "";
50
+ isOAuthRequest() {
51
+ if (getRequestHeader(REFLECT_API_TOKEN_HEADER) || getRequestHeader(API_KEY_HEADER)) {
52
+ return false;
53
+ }
54
+ const authHeader = getRequestHeader(AUTHORIZATION_HEADER);
55
+ if (!authHeader) {
56
+ return false;
57
+ }
58
+ const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;
59
+ return headerValue.toLowerCase().startsWith("bearer ");
52
60
  }
53
- getHeaders() {
61
+ getAuthHeader() {
54
62
  const token = this.getAuthToken();
55
63
  if (!token) {
56
64
  throw new Error("Reflect API token not found");
57
65
  }
66
+ if (this.isOAuthRequest()) {
67
+ return { Authorization: `Bearer ${token}` };
68
+ }
69
+ return { [API_KEY_HEADER]: token };
70
+ }
71
+ getHeaders() {
58
72
  return {
59
- [API_KEY_HEADER]: token,
73
+ ...this.getAuthHeader(),
60
74
  "Content-Type": "application/json",
61
75
  "User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
62
76
  };
@@ -99,7 +113,7 @@ class ReflectClient {
99
113
  this.mcpSessionConnections.delete(mcpSessionId);
100
114
  }
101
115
  async registerTools(register, _getInput) {
102
- const tools = [
116
+ const apiOnlyTools = [
103
117
  new ListSuites(this),
104
118
  new ListSuiteExecutions(this),
105
119
  new GetSuiteExecutionStatus(this),
@@ -107,7 +121,9 @@ class ReflectClient {
107
121
  new CancelSuiteExecution(this),
108
122
  new ListTests(this),
109
123
  new RunTest(this),
110
- new GetTestStatus(this),
124
+ new GetTestStatus(this)
125
+ ];
126
+ const oAuthAndAPISupportedTools = [
111
127
  new ListSegments(this),
112
128
  new ConnectToSession(this),
113
129
  new AddPromptStep(this),
@@ -115,6 +131,7 @@ class ReflectClient {
115
131
  new DeletePreviousStep(this),
116
132
  new AddSegment(this)
117
133
  ];
134
+ const tools = this.isOAuthRequest() ? oAuthAndAPISupportedTools : [...oAuthAndAPISupportedTools, ...apiOnlyTools];
118
135
  for (const tool of tools) {
119
136
  register(tool.specification, tool.handle);
120
137
  }
@@ -1,8 +1,14 @@
1
1
  const API_KEY_HEADER = "X-API-KEY";
2
+ const REFLECT_API_TOKEN_HEADER = "Reflect-Api-Token";
3
+ const AUTHORIZATION_HEADER = "Authorization";
2
4
  const API_HOSTNAME = "api.reflect.run";
3
5
  const WEBSOCKET_HOSTNAME = "recording.us-east-1.reflect.run";
6
+ const WEB_APP_HOSTNAME = "app.reflect.run";
4
7
  export {
5
8
  API_HOSTNAME,
6
9
  API_KEY_HEADER,
7
- WEBSOCKET_HOSTNAME
10
+ AUTHORIZATION_HEADER,
11
+ REFLECT_API_TOKEN_HEADER,
12
+ WEBSOCKET_HOSTNAME,
13
+ WEB_APP_HOSTNAME
8
14
  };
@@ -43,7 +43,7 @@ class ConnectToSession extends Tool {
43
43
  }
44
44
  const wsManager = new WebSocketManager(
45
45
  sessionId,
46
- this.client.getApiToken()
46
+ this.client.getAuthHeader()
47
47
  );
48
48
  try {
49
49
  await wsManager.connect();
@@ -13,17 +13,25 @@ class GetScreenshot extends Tool {
13
13
  type: z.string(),
14
14
  description: "The ID of the Reflect recording session",
15
15
  required: true
16
+ },
17
+ {
18
+ name: "format",
19
+ type: z.enum(["png", "jpeg"]),
20
+ description: "The image format for the screenshot (png or jpeg)",
21
+ required: false
16
22
  }
17
23
  ]
18
24
  };
19
25
  handle = async (args) => {
20
- const { sessionId } = args;
26
+ const { sessionId, format } = args;
21
27
  if (!sessionId) throw new ToolError("sessionId argument is required");
28
+ const imageFormat = format ?? "png";
22
29
  const wsManager = this.client.getConnectedSession(sessionId);
23
30
  const id = randomUUID();
24
31
  const responsePromise = wsManager.waitForResponse(id);
25
32
  await wsManager.sendMcpMessage({
26
33
  type: "mcp:get-screenshot",
34
+ format: imageFormat,
27
35
  id
28
36
  });
29
37
  const response = await responsePromise;
@@ -36,14 +44,15 @@ class GetScreenshot extends Tool {
36
44
  {
37
45
  type: "image",
38
46
  data: imageBase64,
39
- mimeType: "image/png"
47
+ mimeType: `image/${imageFormat}`
40
48
  },
41
49
  {
42
50
  type: "text",
43
51
  text: JSON.stringify({
44
52
  success: true,
45
53
  message: "Screenshot captured",
46
- state
54
+ state,
55
+ format: imageFormat
47
56
  })
48
57
  }
49
58
  ]
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { Tool, ToolError } from "../../../common/tools.js";
3
- import { API_HOSTNAME, API_KEY_HEADER } from "../../config/constants.js";
3
+ import { WEB_APP_HOSTNAME, API_HOSTNAME } from "../../config/constants.js";
4
4
  class ListSegments extends Tool {
5
5
  specification = {
6
6
  title: "List Segments",
@@ -34,13 +34,11 @@ class ListSegments extends Tool {
34
34
  offset = 0,
35
35
  limit = 25
36
36
  } = args;
37
- const url = `https://${API_HOSTNAME}/v1/segments?type=${platform}&offset=${offset}&limit=${limit}`;
37
+ const urlPath = this.client.isOAuthRequest() ? `https://${WEB_APP_HOSTNAME}/api/mcp` : `https://${API_HOSTNAME}/v1`;
38
+ const url = `${urlPath}/segments?type=${platform}&offset=${offset}&limit=${limit}`;
38
39
  const response = await fetch(url, {
39
40
  method: "GET",
40
- headers: {
41
- [API_KEY_HEADER]: this.client.getApiToken(),
42
- "Content-Type": "application/json"
43
- }
41
+ headers: this.client.getHeaders()
44
42
  });
45
43
  if (!response.ok) {
46
44
  throw new ToolError(
@@ -1,13 +1,13 @@
1
1
  import WebSocket from "ws";
2
- import { WEBSOCKET_HOSTNAME, API_KEY_HEADER } from "./config/constants.js";
2
+ import { WEBSOCKET_HOSTNAME } from "./config/constants.js";
3
3
  class WebSocketManager {
4
4
  sessionId;
5
- apiKey;
5
+ authHeader;
6
6
  mcpSocket = null;
7
7
  pendingResponses = /* @__PURE__ */ new Map();
8
- constructor(sessionId, apiKey) {
8
+ constructor(sessionId, authHeader) {
9
9
  this.sessionId = sessionId;
10
- this.apiKey = apiKey;
10
+ this.authHeader = authHeader;
11
11
  }
12
12
  async connect() {
13
13
  if (this.mcpSocket?.readyState === WebSocket.OPEN) {
@@ -26,7 +26,7 @@ class WebSocketManager {
26
26
  const url = `wss://${WEBSOCKET_HOSTNAME}/websocket/v2/recordings/${this.sessionId}/topics/mcp?sid=${this.sessionId}`;
27
27
  this.mcpSocket = new WebSocket(url, {
28
28
  headers: {
29
- [API_KEY_HEADER]: this.apiKey
29
+ ...this.authHeader
30
30
  }
31
31
  });
32
32
  this.mcpSocket.on("open", () => {
@@ -623,15 +623,18 @@ class SwaggerAPI {
623
623
  }
624
624
  /**
625
625
  * Standardize and fix an API definition using AI
626
- * @param params Parameters including owner, API name, and version
626
+ * @param params Parameters including owner, API name, version, and optional newVersion
627
627
  * @returns Standardization response with status and fixed definition
628
628
  */
629
629
  async standardizeApi(params) {
630
+ const searchParams = new URLSearchParams();
631
+ if (params.newVersion) searchParams.set("newVersion", params.newVersion);
632
+ const queryString = searchParams.toString();
630
633
  const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(
631
634
  params.owner
632
635
  )}/${encodeURIComponent(params.api)}/${encodeURIComponent(
633
636
  params.version
634
- )}/standardize`;
637
+ )}/standardize${queryString ? `?${queryString}` : ""}`;
635
638
  const response = await fetch(url, {
636
639
  method: "POST",
637
640
  headers: this.headers
@@ -57,7 +57,10 @@ const CreateApiFromPromptParamsSchema = z.object({
57
57
  const StandardizeApiParamsSchema = z.object({
58
58
  owner: z.string().describe("API owner (organization or user, case-sensitive)"),
59
59
  api: z.string().describe("API name (case-sensitive)"),
60
- version: z.string().describe("Version identifier")
60
+ version: z.string().describe("Version identifier"),
61
+ newVersion: z.string().optional().describe(
62
+ "The version to save the fixed definition as (e.g. '1.0.1'). Omitting this will overwrite the current version — prefer providing a patch bump (e.g. '1.0.0' → '1.0.1') unless the user specifies otherwise."
63
+ )
61
64
  });
62
65
  export {
63
66
  ApiDefinitionParamsSchema,
@@ -140,7 +140,7 @@ const TOOLS = [
140
140
  },
141
141
  {
142
142
  title: "Standardize API",
143
- summary: "Standardize and fix an API definition using AI to ensure compliance with governance policies. Scans the API definition for standardization errors and automatically fixes them using SmartBear AI. If errors are found, they will be sent to SmartBear AI to generate a corrected definition, which is then saved back to the registry. Returns the number of errors found and the fixed definition if successful. Use this tool when users ask to standardize, fix, govern, or ensure governance compliance of APIs.",
143
+ summary: "Standardize and fix an API definition using AI to ensure compliance with governance policies. Scans the API definition for standardization errors and automatically fixes them using SmartBear AI. Optionally provide 'newVersion' (e.g. patch bump '1.0.0' '1.0.1') to save the fixed definition as a new version omitting it will overwrite the current version. Returns the number of errors found and the fixed definition if successful. Use this tool when users ask to standardize, fix, govern, or ensure governance compliance of APIs.",
144
144
  inputSchema: StandardizeApiParamsSchema,
145
145
  handler: "standardizeApi"
146
146
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartbear/mcp",
3
- "version": "0.18.3",
3
+ "version": "0.19.0",
4
4
  "description": "MCP server for interacting SmartBear Products",
5
5
  "keywords": [
6
6
  "smartbear",