@smartbear/mcp 0.13.2 → 0.13.4

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.
@@ -0,0 +1,67 @@
1
+ import { ToolError } from "./tools.js";
2
+ async function executeSamplingOrPolyfill(server, prompt, maxTokens = 1e3) {
3
+ if (!server.isSamplingSupported()) {
4
+ return createPolyfillResult(prompt);
5
+ }
6
+ try {
7
+ const response = await server.server.createMessage({
8
+ messages: [
9
+ {
10
+ role: "user",
11
+ content: {
12
+ type: "text",
13
+ text: prompt
14
+ }
15
+ }
16
+ ],
17
+ maxTokens
18
+ });
19
+ const content = response.content;
20
+ if (content.type !== "text") {
21
+ throw new ToolError(
22
+ `Received unexpected response type from sampling: ${content.type}`
23
+ );
24
+ }
25
+ return content.text;
26
+ } catch (error) {
27
+ console.error(error);
28
+ return createPolyfillResult(prompt);
29
+ }
30
+ }
31
+ function createPolyfillResult(prompt) {
32
+ return {
33
+ requiresPromptExecution: true,
34
+ prompt,
35
+ instructions: "Please execute the above prompt using your AI capabilities and re-request this tool with the result. Include the prompt result in your next request to continue the operation."
36
+ };
37
+ }
38
+ function isSamplingPolyfillResult(value) {
39
+ return typeof value === "object" && value !== null && "requiresPromptExecution" in value && value.requiresPromptExecution === true;
40
+ }
41
+ async function executeElicitationOrPolyfill(server, params, options) {
42
+ if (!server.isElicitationSupported()) {
43
+ return createElicitationPolyfillResult(params);
44
+ }
45
+ try {
46
+ return await server.server.elicitInput(params, options);
47
+ } catch (error) {
48
+ console.error(error);
49
+ return createElicitationPolyfillResult(params);
50
+ }
51
+ }
52
+ function createElicitationPolyfillResult(params) {
53
+ return {
54
+ requiresInputCollection: true,
55
+ inputRequest: params,
56
+ instructions: "Please collect the requested input from the user and re-request this tool with the collected values. Include the input results in your next request to continue the operation."
57
+ };
58
+ }
59
+ function isElicitationPolyfillResult(value) {
60
+ return typeof value === "object" && value !== null && "requiresInputCollection" in value && value.requiresInputCollection === true;
61
+ }
62
+ export {
63
+ executeElicitationOrPolyfill,
64
+ executeSamplingOrPolyfill,
65
+ isElicitationPolyfillResult,
66
+ isSamplingPolyfillResult
67
+ };
@@ -3,10 +3,13 @@ import { ZodObject, ZodIntersection, ZodOptional, ZodDefault, ZodRecord, ZodStri
3
3
  import Bugsnag from "./bugsnag.js";
4
4
  import { CacheService } from "./cache.js";
5
5
  import { MCP_SERVER_VERSION, MCP_SERVER_NAME } from "./info.js";
6
+ import { executeElicitationOrPolyfill, isElicitationPolyfillResult } from "./pollyfills.js";
6
7
  import { ToolError } from "./tools.js";
7
8
  import { unwrapZodType } from "./zod-utils.js";
8
9
  class SmartBearMcpServer extends McpServer {
9
10
  cache;
11
+ samplingSupported = false;
12
+ elicitationSupported = false;
10
13
  constructor() {
11
14
  super(
12
15
  {
@@ -31,6 +34,18 @@ class SmartBearMcpServer extends McpServer {
31
34
  getCache() {
32
35
  return this.cache;
33
36
  }
37
+ setSamplingSupported(supported) {
38
+ this.samplingSupported = supported;
39
+ }
40
+ isSamplingSupported() {
41
+ return this.samplingSupported;
42
+ }
43
+ setElicitationSupported(supported) {
44
+ this.elicitationSupported = supported;
45
+ }
46
+ isElicitationSupported() {
47
+ return this.elicitationSupported;
48
+ }
34
49
  async addClient(client) {
35
50
  await client.registerTools(
36
51
  (params, cb) => {
@@ -70,18 +85,36 @@ class SmartBearMcpServer extends McpServer {
70
85
  ]
71
86
  };
72
87
  } else {
73
- Bugsnag.notify(e, (event) => {
74
- event.addMetadata("app", { tool: toolName });
75
- event.unhandled = true;
76
- });
88
+ Bugsnag.notify(
89
+ e,
90
+ (event) => {
91
+ event.addMetadata("app", { tool: toolName });
92
+ event.unhandled = true;
93
+ }
94
+ );
77
95
  }
78
96
  throw e;
79
97
  }
80
98
  }
81
99
  );
82
100
  },
83
- (params, options) => {
84
- return this.server.elicitInput(params, options);
101
+ async (params, options) => {
102
+ const result = await executeElicitationOrPolyfill(
103
+ this,
104
+ params,
105
+ options
106
+ );
107
+ if (isElicitationPolyfillResult(result)) {
108
+ const schemaStr = "requestedSchema" in result.inputRequest ? `
109
+
110
+ Schema: ${JSON.stringify(result.inputRequest.requestedSchema, null, 2)}` : "";
111
+ throw new ToolError(
112
+ `Input collection required: ${result.inputRequest.message}${schemaStr}
113
+
114
+ ${result.instructions}`
115
+ );
116
+ }
117
+ return result;
85
118
  }
86
119
  );
87
120
  if (client.registerResources) {
@@ -97,10 +130,13 @@ class SmartBearMcpServer extends McpServer {
97
130
  try {
98
131
  return await cb(url2, variables, extra);
99
132
  } catch (e) {
100
- Bugsnag.notify(e, (event) => {
101
- event.addMetadata("app", { resource: name, url: url2 });
102
- event.unhandled = true;
103
- });
133
+ Bugsnag.notify(
134
+ e,
135
+ (event) => {
136
+ event.addMetadata("app", { resource: name, url: url2 });
137
+ event.unhandled = true;
138
+ }
139
+ );
104
140
  throw e;
105
141
  }
106
142
  }
@@ -136,6 +136,19 @@ async function createNewTransport(req, res, transports) {
136
136
  transports.set(newSessionId, { server, transport });
137
137
  }
138
138
  });
139
+ transport.onmessage = (message) => {
140
+ if ("method" in message && message.method === "initialize") {
141
+ if (message.params?.protocolVersion === "2025-11-25") {
142
+ const clientCapabilities = message.params?.capabilities;
143
+ if (Object.hasOwn(clientCapabilities, "sampling")) {
144
+ server.setSamplingSupported(true);
145
+ }
146
+ if (Object.hasOwn(clientCapabilities, "elicitation")) {
147
+ server.setElicitationSupported(true);
148
+ }
149
+ }
150
+ }
151
+ };
139
152
  transport.onclose = () => {
140
153
  if (transport.sessionId) {
141
154
  console.log(`[MCP] Session closed: ${transport.sessionId}`);
@@ -41,6 +41,19 @@ ${message.join("\n")}` : "No clients support environment variable configuration.
41
41
  }
42
42
  }
43
43
  const transport = new StdioServerTransport();
44
+ transport.onmessage = (message) => {
45
+ if ("method" in message && message.method === "initialize") {
46
+ if (message.params?.protocolVersion === "2025-11-25") {
47
+ const clientCapabilities = message.params?.capabilities;
48
+ if (Object.hasOwn(clientCapabilities, "sampling")) {
49
+ server.setSamplingSupported(true);
50
+ }
51
+ if (Object.hasOwn(clientCapabilities, "elicitation")) {
52
+ server.setElicitationSupported(true);
53
+ }
54
+ }
55
+ }
56
+ };
44
57
  await server.connect(transport);
45
58
  }
46
59
  function getEnvVarName(client, key) {
@@ -1,4 +1,4 @@
1
- const version = "0.13.2";
1
+ const version = "0.13.4";
2
2
  const config = { "mcpServerName": "SmartBear MCP Server" };
3
3
  const packageJson = {
4
4
  version,
@@ -1,27 +1,15 @@
1
+ import { executeSamplingOrPolyfill, isSamplingPolyfillResult } from "../../common/pollyfills.js";
1
2
  import { ToolError } from "../../common/tools.js";
2
3
  import { MatcherRecommendationInputSchema, EndpointMatcherSchema } from "./ai.js";
3
4
  import { OADMatcherPrompt } from "./prompts.js";
4
5
  async function getOADMatcherRecommendations(openAPI, server) {
5
- const matcherResponse = await server.createMessage({
6
- messages: [
7
- {
8
- role: "user",
9
- content: {
10
- type: "text",
11
- text: OADMatcherPrompt.replace("{0}", JSON.stringify(openAPI))
12
- }
13
- }
14
- ],
15
- maxTokens: 1e3
16
- });
17
- const regex = /```json[c5]?([\s\S]*?)```/i;
18
- const content = matcherResponse.content;
19
- if (content.type !== "text") {
20
- throw new Error(
21
- `Received unexpected response type from matcher recommendations: ${content.type}`
22
- );
6
+ const prompt = OADMatcherPrompt.replace("{0}", JSON.stringify(openAPI));
7
+ const response = await executeSamplingOrPolyfill(server, prompt, 1e3);
8
+ if (isSamplingPolyfillResult(response)) {
9
+ return response;
23
10
  }
24
- const match = regex.exec(content.text);
11
+ const regex = /```json[c5]?([\s\S]*?)```/i;
12
+ const match = regex.exec(response);
25
13
  if (match) {
26
14
  const jsonText = match[1].trim();
27
15
  const parsed = JSON.parse(jsonText);
@@ -34,6 +22,9 @@ async function getOADMatcherRecommendations(openAPI, server) {
34
22
  }
35
23
  }
36
24
  async function getUserMatcherSelection(recommendations, getInput) {
25
+ if (isSamplingPolyfillResult(recommendations)) {
26
+ return recommendations;
27
+ }
37
28
  const recommendationsMap = /* @__PURE__ */ new Map();
38
29
  recommendations.forEach((rec, index) => {
39
30
  recommendationsMap.set(`Recommendation ${index + 1}`, JSON.stringify(rec));
@@ -1,5 +1,6 @@
1
1
  import zod__default from "zod";
2
2
  import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
3
+ import { isSamplingPolyfillResult } from "../common/pollyfills.js";
3
4
  import { ToolError } from "../common/tools.js";
4
5
  import { getOADMatcherRecommendations, getUserMatcherSelection } from "./client/prompt-utils.js";
5
6
  import { PROMPTS } from "./client/prompts.js";
@@ -47,7 +48,7 @@ class PactflowClient {
47
48
  }
48
49
  this.baseUrl = config.base_url;
49
50
  this.aiBaseUrl = `${this.baseUrl}/api/ai`;
50
- this._server = server.server;
51
+ this._server = server;
51
52
  }
52
53
  isConfigured() {
53
54
  return this.baseUrl !== void 0;
@@ -58,7 +59,7 @@ class PactflowClient {
58
59
  *
59
60
  * @param toolInput The input data for the generation process.
60
61
  * @param getInput Function to get additional input from the user if needed.
61
- * @returns The result of the generation process.
62
+ * @returns The result of the generation process or a polyfill result requiring prompt execution.
62
63
  * @throws Error if the HTTP request fails or the operation times out.
63
64
  */
64
65
  async generate(toolInput, getInput) {
@@ -67,10 +68,16 @@ class PactflowClient {
67
68
  toolInput.openapi.document,
68
69
  this.server
69
70
  );
71
+ if (isSamplingPolyfillResult(matcherResponse)) {
72
+ return matcherResponse;
73
+ }
70
74
  const userSelection = await getUserMatcherSelection(
71
75
  matcherResponse,
72
76
  getInput
73
77
  );
78
+ if (isSamplingPolyfillResult(userSelection)) {
79
+ return userSelection;
80
+ }
74
81
  toolInput.openapi.matcher = userSelection;
75
82
  }
76
83
  const status_response = await this.submitHttpCallback(
@@ -87,7 +94,7 @@ class PactflowClient {
87
94
  *
88
95
  * @param toolInput The input data for the review process.
89
96
  * @param getInput Function to get additional input from the user if needed.
90
- * @returns The result of the review process.
97
+ * @returns The result of the review process or a polyfill result requiring prompt execution.
91
98
  * @throws Error if the HTTP request fails or the operation times out.
92
99
  */
93
100
  async review(toolInput, getInput) {
@@ -96,10 +103,16 @@ class PactflowClient {
96
103
  toolInput.openapi.document,
97
104
  this.server
98
105
  );
106
+ if (isSamplingPolyfillResult(matcherResponse)) {
107
+ return matcherResponse;
108
+ }
99
109
  const userSelection = await getUserMatcherSelection(
100
110
  matcherResponse,
101
111
  getInput
102
112
  );
113
+ if (isSamplingPolyfillResult(userSelection)) {
114
+ return userSelection;
115
+ }
103
116
  toolInput.openapi.matcher = userSelection;
104
117
  }
105
118
  const status_response = await this.submitHttpCallback("/review", toolInput);
@@ -10,7 +10,7 @@ class SwaggerConfiguration {
10
10
  this.portalBasePath = param.portalBasePath || "https://api.portal.swaggerhub.com/v1";
11
11
  this.registryBasePath = param.registryBasePath || "https://api.swaggerhub.com";
12
12
  this.uiBasePath = param.uiBasePath || "https://app.swaggerhub.com";
13
- this.userManagementBasePath = param.userManagementBasePath || `${this.uiBasePath}/user-management/v1`;
13
+ this.userManagementBasePath = param.userManagementBasePath || `${this.registryBasePath}/user-management/v1`;
14
14
  this.headers = {
15
15
  Authorization: `Bearer ${this.token}`,
16
16
  "Content-Type": "application/json",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartbear/mcp",
3
- "version": "0.13.2",
3
+ "version": "0.13.4",
4
4
  "description": "MCP server for interacting SmartBear Products",
5
5
  "keywords": [
6
6
  "smartbear",