@smartbear/mcp 0.13.2 → 0.13.3
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/dist/common/pollyfills.js +67 -0
- package/dist/common/server.js +46 -10
- package/dist/common/transport-http.js +13 -0
- package/dist/common/transport-stdio.js +13 -0
- package/dist/package.json.js +1 -1
- package/dist/pactflow/client/prompt-utils.js +10 -19
- package/dist/pactflow/client.js +16 -3
- package/package.json +1 -1
|
@@ -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
|
+
};
|
package/dist/common/server.js
CHANGED
|
@@ -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(
|
|
74
|
-
|
|
75
|
-
event
|
|
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
|
-
|
|
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(
|
|
101
|
-
|
|
102
|
-
event
|
|
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) {
|
package/dist/package.json.js
CHANGED
|
@@ -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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
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));
|
package/dist/pactflow/client.js
CHANGED
|
@@ -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
|
|
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);
|