@smartbear/mcp 0.5.0 → 0.7.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 +22 -111
- package/dist/api-hub/client/api.js +253 -0
- package/dist/api-hub/client/configuration.js +27 -0
- package/dist/api-hub/client/index.js +5 -0
- package/dist/api-hub/client/portal-types.js +131 -0
- package/dist/api-hub/client/registry-types.js +55 -0
- package/dist/api-hub/client/tools.js +86 -0
- package/dist/api-hub/client.js +64 -404
- package/dist/bugsnag/client/api/CurrentUser.js +18 -13
- package/dist/bugsnag/client/api/Error.js +35 -35
- package/dist/bugsnag/client/api/Project.js +137 -9
- package/dist/bugsnag/client/api/base.js +27 -13
- package/dist/bugsnag/client/api/filters.js +9 -9
- package/dist/bugsnag/client.js +584 -145
- package/dist/common/info.js +1 -1
- package/dist/common/server.js +44 -27
- package/dist/index.js +13 -6
- package/dist/pactflow/client/ai.js +33 -2
- package/dist/pactflow/client/base.js +51 -3
- package/dist/pactflow/client/prompt-utils.js +89 -0
- package/dist/pactflow/client/prompts.js +131 -0
- package/dist/pactflow/client/tools.js +39 -7
- package/dist/pactflow/client/utils.js +1 -1
- package/dist/pactflow/client.js +147 -9
- package/dist/qmetry/client/api/client-api.js +39 -0
- package/dist/qmetry/client/handlers.js +11 -0
- package/dist/qmetry/client/project.js +27 -0
- package/dist/qmetry/client/testcase.js +104 -0
- package/dist/qmetry/client/tools.js +222 -0
- package/dist/qmetry/client.js +95 -0
- package/dist/qmetry/config/constants.js +12 -0
- package/dist/qmetry/config/rest-endpoints.js +11 -0
- package/dist/qmetry/types/common.js +174 -0
- package/dist/qmetry/types/testcase.js +19 -0
- package/dist/reflect/client.js +14 -14
- package/package.json +9 -6
package/dist/common/info.js
CHANGED
package/dist/common/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1
|
+
import { McpServer, ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { ZodAny, ZodArray, ZodBoolean, ZodEnum, ZodLiteral, ZodNumber, ZodObject, ZodString, ZodUnion, } from "zod";
|
|
3
3
|
import Bugsnag from "../common/bugsnag.js";
|
|
4
4
|
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "./info.js";
|
|
@@ -11,6 +11,10 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
11
11
|
capabilities: {
|
|
12
12
|
resources: { listChanged: true }, // Server supports dynamic resource lists
|
|
13
13
|
tools: { listChanged: true }, // Server supports dynamic tool lists
|
|
14
|
+
sampling: {}, // Server supports sampling requests to Host
|
|
15
|
+
elicitation: {}, // Server supports eliciting input from the user
|
|
16
|
+
logging: {}, // Server supports logging messages
|
|
17
|
+
prompts: {}, // Server supports sending prompts to Host
|
|
14
18
|
},
|
|
15
19
|
});
|
|
16
20
|
}
|
|
@@ -37,7 +41,9 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
37
41
|
});
|
|
38
42
|
if (client.registerResources) {
|
|
39
43
|
client.registerResources((name, path, cb) => {
|
|
40
|
-
return super.registerResource(name, new ResourceTemplate(`${client.prefix}://${name}/${path}`, {
|
|
44
|
+
return super.registerResource(name, new ResourceTemplate(`${client.prefix}://${name}/${path}`, {
|
|
45
|
+
list: undefined,
|
|
46
|
+
}), {}, async (url, variables, extra) => {
|
|
41
47
|
try {
|
|
42
48
|
return await cb(url, variables, extra);
|
|
43
49
|
}
|
|
@@ -48,6 +54,11 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
48
54
|
});
|
|
49
55
|
});
|
|
50
56
|
}
|
|
57
|
+
if (client.registerPrompts) {
|
|
58
|
+
client.registerPrompts((name, config, cb) => {
|
|
59
|
+
return super.registerPrompt(name, config, cb);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
51
62
|
}
|
|
52
63
|
getAnnotations(toolTitle, params) {
|
|
53
64
|
const annotations = {
|
|
@@ -85,64 +96,70 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
85
96
|
return args;
|
|
86
97
|
}
|
|
87
98
|
getDescription(params) {
|
|
88
|
-
const { summary, useCases, examples, parameters, zodSchema, hints, outputFormat } = params;
|
|
99
|
+
const { summary, useCases, examples, parameters, zodSchema, hints, outputFormat, } = params;
|
|
89
100
|
let description = summary;
|
|
90
101
|
// Parameters if available otherwise use zodSchema
|
|
91
102
|
if ((parameters ?? []).length > 0) {
|
|
92
|
-
description += `\n\n**Parameters:**\n${parameters
|
|
93
|
-
|
|
94
|
-
`${p.
|
|
95
|
-
`${p.
|
|
103
|
+
description += `\n\n**Parameters:**\n${parameters
|
|
104
|
+
?.map((p) => `- ${p.name} (${this.getReadableTypeName(p.type)})${p.required ? " *required*" : ""}` +
|
|
105
|
+
`${p.description ? `: ${p.description}` : ""}` +
|
|
106
|
+
`${p.examples ? ` (e.g. ${p.examples.join(", ")})` : ""}` +
|
|
107
|
+
`${p.constraints ? `\n - ${p.constraints.join("\n - ")}` : ""}`)
|
|
108
|
+
.join("\n")}`;
|
|
96
109
|
}
|
|
97
110
|
if (zodSchema && zodSchema instanceof ZodObject) {
|
|
98
111
|
description += "\n\n**Parameters:**\n";
|
|
99
112
|
description += Object.keys(zodSchema.shape)
|
|
100
|
-
.map(key => this.formatParameterDescription(key, zodSchema.shape[key]))
|
|
101
|
-
.join(
|
|
113
|
+
.map((key) => this.formatParameterDescription(key, zodSchema.shape[key]))
|
|
114
|
+
.join("\n");
|
|
102
115
|
}
|
|
103
116
|
if (outputFormat) {
|
|
104
117
|
description += `\n\n**Output Format:** ${outputFormat}`;
|
|
105
118
|
}
|
|
106
119
|
// Use Cases
|
|
107
120
|
if (useCases && useCases.length > 0) {
|
|
108
|
-
description += `\n\n**Use Cases:** ${useCases.map((uc, i) => `${i + 1}. ${uc}`).join(
|
|
121
|
+
description += `\n\n**Use Cases:** ${useCases.map((uc, i) => `${i + 1}. ${uc}`).join(" ")}`;
|
|
109
122
|
}
|
|
110
123
|
// Examples
|
|
111
124
|
if (examples && examples.length > 0) {
|
|
112
|
-
description +=
|
|
125
|
+
description +=
|
|
126
|
+
`\n\n**Examples:**\n` +
|
|
127
|
+
examples
|
|
128
|
+
.map((ex, idx) => `${idx + 1}. ${ex.description}\n\`\`\`json\n${JSON.stringify(ex.parameters, null, 2)}\n\`\`\`${ex.expectedOutput ? `\nExpected Output: ${ex.expectedOutput}` : ""}`)
|
|
129
|
+
.join("\n\n");
|
|
113
130
|
}
|
|
114
131
|
// Hints
|
|
115
132
|
if (hints && hints.length > 0) {
|
|
116
|
-
description += `\n\n**Hints:** ${hints.map((hint, i) => `${i + 1}. ${hint}`).join(
|
|
133
|
+
description += `\n\n**Hints:** ${hints.map((hint, i) => `${i + 1}. ${hint}`).join(" ")}`;
|
|
117
134
|
}
|
|
118
135
|
return description.trim();
|
|
119
136
|
}
|
|
120
137
|
formatParameterDescription(key, field) {
|
|
121
|
-
return `- ${key} (${this.getReadableTypeName(field)})` +
|
|
122
|
-
`${field.isOptional() ?
|
|
123
|
-
`${field.description ? `: ${field.description}` :
|
|
124
|
-
`${key === "examples" && field instanceof ZodEnum ? ` (e.g. ${Object.keys(field.enum).join(
|
|
125
|
-
`${key === "constraints" && field instanceof ZodEnum ? `\n - ${Object.keys(field.enum).join(
|
|
138
|
+
return (`- ${key} (${this.getReadableTypeName(field)})` +
|
|
139
|
+
`${field.isOptional() ? "" : " *required*"}` +
|
|
140
|
+
`${field.description ? `: ${field.description}` : ""}` +
|
|
141
|
+
`${key === "examples" && field instanceof ZodEnum ? ` (e.g. ${Object.keys(field.enum).join(", ")})` : ""}` +
|
|
142
|
+
`${key === "constraints" && field instanceof ZodEnum ? `\n - ${Object.keys(field.enum).join("\n - ")}` : ""}`);
|
|
126
143
|
}
|
|
127
144
|
getReadableTypeName(zodType) {
|
|
128
145
|
if (zodType instanceof ZodString)
|
|
129
|
-
return
|
|
146
|
+
return "string";
|
|
130
147
|
if (zodType instanceof ZodNumber)
|
|
131
|
-
return
|
|
148
|
+
return "number";
|
|
132
149
|
if (zodType instanceof ZodBoolean)
|
|
133
|
-
return
|
|
150
|
+
return "boolean";
|
|
134
151
|
if (zodType instanceof ZodArray)
|
|
135
|
-
return
|
|
152
|
+
return "array";
|
|
136
153
|
if (zodType instanceof ZodObject)
|
|
137
|
-
return
|
|
154
|
+
return "object";
|
|
138
155
|
if (zodType instanceof ZodEnum)
|
|
139
|
-
return
|
|
156
|
+
return "enum";
|
|
140
157
|
if (zodType instanceof ZodLiteral)
|
|
141
|
-
return
|
|
158
|
+
return "literal";
|
|
142
159
|
if (zodType instanceof ZodUnion)
|
|
143
|
-
return
|
|
160
|
+
return "union";
|
|
144
161
|
if (zodType instanceof ZodAny)
|
|
145
|
-
return
|
|
146
|
-
return
|
|
162
|
+
return "any";
|
|
163
|
+
return "any";
|
|
147
164
|
}
|
|
148
165
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import Bugsnag from "./common/bugsnag.js";
|
|
4
|
-
import { BugsnagClient } from "./bugsnag/client.js";
|
|
5
|
-
import { ReflectClient } from "./reflect/client.js";
|
|
6
3
|
import { ApiHubClient } from "./api-hub/client.js";
|
|
4
|
+
import { BugsnagClient } from "./bugsnag/client.js";
|
|
5
|
+
import Bugsnag from "./common/bugsnag.js";
|
|
7
6
|
import { SmartBearMcpServer } from "./common/server.js";
|
|
8
7
|
import { PactflowClient } from "./pactflow/client.js";
|
|
8
|
+
import { QmetryClient } from "./qmetry/client.js";
|
|
9
|
+
import { ReflectClient } from "./reflect/client.js";
|
|
9
10
|
// This is used to report errors in the MCP server itself
|
|
10
11
|
// If you want to use your own BugSnag API key, set the MCP_SERVER_BUGSNAG_API_KEY environment variable
|
|
11
12
|
const McpServerBugsnagAPIKey = process.env.MCP_SERVER_BUGSNAG_API_KEY;
|
|
@@ -21,6 +22,8 @@ async function main() {
|
|
|
21
22
|
const pactBrokerUrl = process.env.PACT_BROKER_BASE_URL;
|
|
22
23
|
const pactBrokerUsername = process.env.PACT_BROKER_USERNAME;
|
|
23
24
|
const pactBrokerPassword = process.env.PACT_BROKER_PASSWORD;
|
|
25
|
+
const qmetryToken = process.env.QMETRY_API_KEY;
|
|
26
|
+
const qmetryBaseUrl = process.env.QMETRY_BASE_URL;
|
|
24
27
|
let client_defined = false;
|
|
25
28
|
if (reflectToken) {
|
|
26
29
|
server.addClient(new ReflectClient(reflectToken));
|
|
@@ -38,19 +41,23 @@ async function main() {
|
|
|
38
41
|
}
|
|
39
42
|
if (pactBrokerUrl) {
|
|
40
43
|
if (pactBrokerToken) {
|
|
41
|
-
server.addClient(new PactflowClient(pactBrokerToken, pactBrokerUrl, "pactflow"));
|
|
44
|
+
server.addClient(new PactflowClient(pactBrokerToken, pactBrokerUrl, "pactflow", server.server));
|
|
42
45
|
client_defined = true;
|
|
43
46
|
}
|
|
44
47
|
else if (pactBrokerUsername && pactBrokerPassword) {
|
|
45
|
-
server.addClient(new PactflowClient({ username: pactBrokerUsername, password: pactBrokerPassword }, pactBrokerUrl, "pact_broker"));
|
|
48
|
+
server.addClient(new PactflowClient({ username: pactBrokerUsername, password: pactBrokerPassword }, pactBrokerUrl, "pact_broker", server.server));
|
|
46
49
|
client_defined = true;
|
|
47
50
|
}
|
|
48
51
|
else {
|
|
49
52
|
console.error("If the Pact Broker base URL is specified, you must specify either (a) a PactFlow token, or (b) a Pact Broker username and password pair.");
|
|
50
53
|
}
|
|
51
54
|
}
|
|
55
|
+
if (qmetryToken) {
|
|
56
|
+
server.addClient(new QmetryClient(qmetryToken, qmetryBaseUrl));
|
|
57
|
+
client_defined = true;
|
|
58
|
+
}
|
|
52
59
|
if (!client_defined) {
|
|
53
|
-
console.error("Please set one of REFLECT_API_TOKEN, BUGSNAG_AUTH_TOKEN, API_HUB_API_KEY or PACT_BROKER_BASE_URL / (and relevant Pact auth) environment variables");
|
|
60
|
+
console.error("Please set one of REFLECT_API_TOKEN, BUGSNAG_AUTH_TOKEN, API_HUB_API_KEY, QMETRY_API_KEY or PACT_BROKER_BASE_URL / (and relevant Pact auth) environment variables");
|
|
54
61
|
process.exit(1);
|
|
55
62
|
}
|
|
56
63
|
const transport = new StdioServerTransport();
|
|
@@ -78,7 +78,7 @@ export const EndpointMatcherSchema = z
|
|
|
78
78
|
.optional()
|
|
79
79
|
.describe("OpenAPI operation ID to match (e.g., 'getUserById', 'get*'). Supports glob patterns"),
|
|
80
80
|
})
|
|
81
|
-
.
|
|
81
|
+
.optional()
|
|
82
82
|
.describe("REQUIRED: Matcher to specify which endpoints from the OpenAPI document to generate tests for. At least one matcher field must be provided");
|
|
83
83
|
export const RemoteOpenAPIDocumentSchema = z
|
|
84
84
|
.object({
|
|
@@ -104,7 +104,8 @@ export const OpenAPIWithMatcherSchema = z
|
|
|
104
104
|
matcher: EndpointMatcherSchema,
|
|
105
105
|
remoteDocument: RemoteOpenAPIDocumentSchema.optional().describe("The remote OpenAPI document to use for the review/generation in case openapi document is not provided. If provided do not include the document field under openapi."),
|
|
106
106
|
})
|
|
107
|
-
.describe("If provided, the OpenAPI document which describes the API being tested and is accompanied by a matcher which will be used to identify the interactions in the OpenAPI document which are relevant to the Pact refinement process.")
|
|
107
|
+
.describe("If provided, the OpenAPI document which describes the API being tested and is accompanied by a matcher which will be used to identify the interactions in the OpenAPI document which are relevant to the Pact refinement process.")
|
|
108
|
+
.transform(addOpenAPISpecToSchema);
|
|
108
109
|
export const RefineInputSchema = z.object({
|
|
109
110
|
pactTests: FileInputSchema.describe("Primary pact tests that needs to be refined."),
|
|
110
111
|
code: z
|
|
@@ -144,3 +145,33 @@ export const GenerationInputSchema = z.object({
|
|
|
144
145
|
.describe("Optional free-form instructions to guide the generation process (e.g., 'Focus on error scenarios', 'Include authentication headers', 'Use specific test framework patterns')"),
|
|
145
146
|
testTemplate: FileInputSchema.optional().describe("Optional test template to use as a basis for generation. Helps ensure generated tests follow your specific patterns, frameworks, and coding standards"),
|
|
146
147
|
});
|
|
148
|
+
export const MatcherRecommendationInputSchema = z.array(EndpointMatcherSchema);
|
|
149
|
+
export const AiCreditsSchema = z
|
|
150
|
+
.object({
|
|
151
|
+
total: z.number().describe("The total number of AI credits available."),
|
|
152
|
+
used: z.number().describe("The number of AI credits used."),
|
|
153
|
+
})
|
|
154
|
+
.describe("AI credits information.");
|
|
155
|
+
export const OrganizationEntitlementsSchema = z
|
|
156
|
+
.object({
|
|
157
|
+
name: z.string().describe("The name of the organization."),
|
|
158
|
+
planAiEnabled: z
|
|
159
|
+
.boolean()
|
|
160
|
+
.describe("Whether AI features are enabled at the plan level."),
|
|
161
|
+
preferencesAiEnabled: z
|
|
162
|
+
.boolean()
|
|
163
|
+
.describe("Whether AI features are enabled at the preferences level."),
|
|
164
|
+
aiCredits: AiCreditsSchema.describe("AI credits information."),
|
|
165
|
+
})
|
|
166
|
+
.describe("Organization entitlements information.");
|
|
167
|
+
export const UserEntitlementsSchema = z
|
|
168
|
+
.object({
|
|
169
|
+
aiPermissions: z.array(z.string()).describe("List of AI permissions."),
|
|
170
|
+
})
|
|
171
|
+
.describe("User entitlements information.");
|
|
172
|
+
export const EntitlementsSchema = z
|
|
173
|
+
.object({
|
|
174
|
+
organizationEntitlements: OrganizationEntitlementsSchema.describe("Organization entitlements information."),
|
|
175
|
+
userEntitlements: UserEntitlementsSchema.describe("User entitlements information."),
|
|
176
|
+
})
|
|
177
|
+
.describe("Entitlements information.");
|
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export const CanIDeploySchema = z.object({
|
|
3
|
-
pacticipant: z
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
pacticipant: z
|
|
4
|
+
.string()
|
|
5
|
+
.describe("The name of the pacticipant (application/service) being evaluated for deployment"),
|
|
6
|
+
version: z
|
|
7
|
+
.string()
|
|
8
|
+
.describe("The version of the pacticipant that you want to check if it's safe to deploy"),
|
|
9
|
+
environment: z
|
|
10
|
+
.string()
|
|
11
|
+
.describe("The target environment where the pacticipant version will be deployed (e.g., 'production', 'staging', 'test')"),
|
|
12
|
+
});
|
|
13
|
+
export const MatrixSchema = z.object({
|
|
14
|
+
latestby: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("This property removes the rows for the overridden pacts/verifications from the results. The options are cvp (show only the latest row for each consumer version and provider) and cvpv (show only the latest row each consumer version and provider version). For a can-i-deploy query with one selector, it should be set to cvp. For a can-i-deploy query with two selectors, it should be set to cvpv."),
|
|
18
|
+
limit: z
|
|
19
|
+
.number()
|
|
20
|
+
.min(1)
|
|
21
|
+
.max(1000)
|
|
22
|
+
.default(100)
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("The limit on the number of results to return (1-1000, default: 100)"),
|
|
25
|
+
q: z
|
|
26
|
+
.array(z.object({
|
|
27
|
+
pacticipant: z
|
|
28
|
+
.string()
|
|
29
|
+
.describe("Name of the pacticipant (application)"),
|
|
30
|
+
version: z.string().optional().describe("Version number"),
|
|
31
|
+
branch: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Name of the pacticipant version branch"),
|
|
35
|
+
environment: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("The name of the environment that the pacticipant version is deployed to"),
|
|
39
|
+
latest: z
|
|
40
|
+
.boolean()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Used in conjunction with other properties to indicate whether the selector is describing the latest version from a branch/with a tag/for a pacticipant, or all of them. Note that when used with tags, the 'latest' is calculated using the creation date of the pacticipant version, NOT the creation date of the tag."),
|
|
43
|
+
tag: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("The name of the pacticipant version tag (superseded by branch and environments)"),
|
|
47
|
+
mainBranch: z
|
|
48
|
+
.boolean()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("Whether or not the version(s) described are from the main branch of the pacticipant, as set in the mainBranch property of the pacticipant resource."),
|
|
51
|
+
}))
|
|
52
|
+
.min(1)
|
|
53
|
+
.max(2),
|
|
6
54
|
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { EndpointMatcherSchema, MatcherRecommendationInputSchema, } from "./ai.js";
|
|
2
|
+
import { OADMatcherPrompt } from "./prompts.js";
|
|
3
|
+
/**
|
|
4
|
+
* Get OpenAPI matcher recommendations using sampling.
|
|
5
|
+
*
|
|
6
|
+
* @param openAPI The OpenAPI document to analyze.
|
|
7
|
+
* @param server The SmartBear MCP server instance.
|
|
8
|
+
* @returns A promise that resolves to the matcher recommendations.
|
|
9
|
+
* @throws Error if unable to parse recommendations.
|
|
10
|
+
*/
|
|
11
|
+
export async function getOADMatcherRecommendations(openAPI, server) {
|
|
12
|
+
const matcherResponse = await server.createMessage({
|
|
13
|
+
messages: [
|
|
14
|
+
{
|
|
15
|
+
role: "user",
|
|
16
|
+
content: {
|
|
17
|
+
type: "text",
|
|
18
|
+
text: OADMatcherPrompt.replace("{0}", JSON.stringify(openAPI)),
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
maxTokens: 1000,
|
|
23
|
+
});
|
|
24
|
+
const regex = /```json[c5]?([\s\S]*?)```/i;
|
|
25
|
+
const match = regex.exec(matcherResponse.content.text);
|
|
26
|
+
if (match) {
|
|
27
|
+
const jsonText = match[1].trim();
|
|
28
|
+
const parsed = JSON.parse(jsonText);
|
|
29
|
+
const matcherRecommendations = MatcherRecommendationInputSchema.parse(parsed);
|
|
30
|
+
return matcherRecommendations;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw new Error("Unable to parse recommendations please provide OpenAPI matchers manually.");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get user selection for matcher recommendations.
|
|
38
|
+
*
|
|
39
|
+
* @param recommendations The list of matcher recommendations.
|
|
40
|
+
* @param getInput The function to get user input.
|
|
41
|
+
* @returns The selected endpoint matcher.
|
|
42
|
+
*/
|
|
43
|
+
export async function getUserMatcherSelection(recommendations, getInput) {
|
|
44
|
+
const recommendationsMap = new Map();
|
|
45
|
+
recommendations.forEach((rec, index) => {
|
|
46
|
+
recommendationsMap.set(`Recommendation ${index + 1}`, JSON.stringify(rec));
|
|
47
|
+
});
|
|
48
|
+
const result = await getInput({
|
|
49
|
+
message: `Select one of the generated matchers you would want to use \n\n ${recommendations
|
|
50
|
+
.map((rec, index) => `\n\nRecommendation ${index + 1}: \n\n\n ${JSON.stringify(rec)}`)
|
|
51
|
+
.join("\n\n")}`,
|
|
52
|
+
requestedSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
generatedMatchers: {
|
|
56
|
+
type: "string",
|
|
57
|
+
title: "Generated Matchers",
|
|
58
|
+
description: "Use the matchers generated for the OpenAPI document",
|
|
59
|
+
enum: recommendations.map((_, index) => `Recommendation ${index + 1}`),
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
required: ["generatedMatchers"],
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
if (result.action === "accept") {
|
|
66
|
+
return EndpointMatcherSchema.parse(JSON.parse(recommendationsMap.get(result.content?.generatedMatchers) ||
|
|
67
|
+
""));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const result = await getInput({
|
|
71
|
+
message: "Enter the matchers you would want to use for the OpenAPI document",
|
|
72
|
+
requestedSchema: {
|
|
73
|
+
type: "object",
|
|
74
|
+
properties: {
|
|
75
|
+
enteredMatchers: {
|
|
76
|
+
type: "string",
|
|
77
|
+
title: "Enter the matchers you would want to use for the OpenAPI document",
|
|
78
|
+
description: "Enter the matchers you would want to use for the OpenAPI document",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
required: ["enteredMatchers"],
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
if (result.action === "accept") {
|
|
85
|
+
return EndpointMatcherSchema.parse(JSON.parse(result.content?.enteredMatchers));
|
|
86
|
+
}
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
3
|
+
import { EndpointMatcherSchema } from "./ai.js";
|
|
4
|
+
const OADMatcherPromptOpenAPIDocExample = {
|
|
5
|
+
openapi: "3.1.0",
|
|
6
|
+
info: {
|
|
7
|
+
title: "My API",
|
|
8
|
+
version: "1.0.0",
|
|
9
|
+
description: "A sample API for demonstration purposes.",
|
|
10
|
+
},
|
|
11
|
+
paths: {
|
|
12
|
+
"/users": {
|
|
13
|
+
get: {
|
|
14
|
+
summary: "Get all users",
|
|
15
|
+
responses: {
|
|
16
|
+
"200": {
|
|
17
|
+
description: "A list of users",
|
|
18
|
+
content: {
|
|
19
|
+
"application/json": {
|
|
20
|
+
schema: {
|
|
21
|
+
type: "array",
|
|
22
|
+
items: {
|
|
23
|
+
$ref: "#/components/schemas/User",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
components: {
|
|
34
|
+
schemas: {
|
|
35
|
+
User: {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
id: {
|
|
39
|
+
type: "integer",
|
|
40
|
+
format: "int64",
|
|
41
|
+
},
|
|
42
|
+
name: {
|
|
43
|
+
type: "string",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
const OADMatcherPromptRecommendationExample = [
|
|
51
|
+
{
|
|
52
|
+
path: "/users",
|
|
53
|
+
methods: ["GET"],
|
|
54
|
+
statusCodes: [200, "2XX"],
|
|
55
|
+
operationId: "get*",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
path: "/users/*",
|
|
59
|
+
methods: ["GET"],
|
|
60
|
+
statusCodes: ["2XX"],
|
|
61
|
+
operationId: "*User*",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
path: "/users",
|
|
65
|
+
methods: ["GET"],
|
|
66
|
+
statusCodes: [200],
|
|
67
|
+
operationId: "getAllUsers",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
path: "/users/**",
|
|
71
|
+
methods: ["GET"],
|
|
72
|
+
statusCodes: ["2XX", 404],
|
|
73
|
+
operationId: "get*",
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
export const OADMatcherPrompt = `
|
|
77
|
+
|
|
78
|
+
Generate a list of recommendations(maximum of 5) in JSON to use with an OpenAPI matcher.
|
|
79
|
+
Zod Schema for the matcher to be generated is provided below in the markdown block of javascript use this to generate the recommendations for the matcher. Recommendations should contain all the fields from the schema and only output the JSON in a markdown formatted block.
|
|
80
|
+
|
|
81
|
+
\`\`\`javascript
|
|
82
|
+
const EndpointMatcherSchema = ${JSON.stringify(zodToJsonSchema(EndpointMatcherSchema))};
|
|
83
|
+
\`\`\`
|
|
84
|
+
|
|
85
|
+
Example OpenAPI document:-
|
|
86
|
+
|
|
87
|
+
if OpenAPI document provided is:-
|
|
88
|
+
|
|
89
|
+
\`\`\`json
|
|
90
|
+
${JSON.stringify(OADMatcherPromptOpenAPIDocExample, null, 2)}
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
Generated recommendations are:-
|
|
94
|
+
|
|
95
|
+
\`\`\`json
|
|
96
|
+
${JSON.stringify(OADMatcherPromptRecommendationExample, null, 2)}
|
|
97
|
+
\`\`\`
|
|
98
|
+
|
|
99
|
+
Actual OpenAPI document:-
|
|
100
|
+
|
|
101
|
+
Now provided the below OpenAPI document:-
|
|
102
|
+
|
|
103
|
+
\`\`\`json
|
|
104
|
+
{0}
|
|
105
|
+
\`\`\`
|
|
106
|
+
|
|
107
|
+
Give JSON recommendations only provide the JSON block in markdown don't include any additional text.
|
|
108
|
+
`;
|
|
109
|
+
export const PROMPTS = [
|
|
110
|
+
{
|
|
111
|
+
name: "OpenAPI Matcher recommendations",
|
|
112
|
+
params: {
|
|
113
|
+
description: "Get OpenAPI matcher recommendations using sampling",
|
|
114
|
+
title: "OpenAPI Matcher recommendations",
|
|
115
|
+
argsSchema: {
|
|
116
|
+
openAPI: z.string(),
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
callback: ({ openAPI }) => ({
|
|
120
|
+
messages: [
|
|
121
|
+
{
|
|
122
|
+
role: "user",
|
|
123
|
+
content: {
|
|
124
|
+
type: "text",
|
|
125
|
+
text: OADMatcherPrompt.replace("{0}", openAPI),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
}),
|
|
130
|
+
},
|
|
131
|
+
];
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { GenerationInputSchema, RefineInputSchema } from "./ai.js";
|
|
14
|
-
import { CanIDeploySchema } from "./base.js";
|
|
14
|
+
import { CanIDeploySchema, MatrixSchema } from "./base.js";
|
|
15
15
|
export const TOOLS = [
|
|
16
16
|
{
|
|
17
17
|
title: "Generate Pact Tests",
|
|
@@ -20,6 +20,7 @@ export const TOOLS = [
|
|
|
20
20
|
zodSchema: GenerationInputSchema,
|
|
21
21
|
handler: "generate",
|
|
22
22
|
clients: ["pactflow"], // ONLY pactflow
|
|
23
|
+
enableElicitation: true,
|
|
23
24
|
},
|
|
24
25
|
{
|
|
25
26
|
title: "Review Pact Tests",
|
|
@@ -27,7 +28,8 @@ export const TOOLS = [
|
|
|
27
28
|
purpose: "Review Pact tests for API interactions",
|
|
28
29
|
zodSchema: RefineInputSchema,
|
|
29
30
|
handler: "review",
|
|
30
|
-
clients: ["pactflow"]
|
|
31
|
+
clients: ["pactflow"],
|
|
32
|
+
enableElicitation: true,
|
|
31
33
|
},
|
|
32
34
|
{
|
|
33
35
|
title: "Get Provider States",
|
|
@@ -38,11 +40,11 @@ export const TOOLS = [
|
|
|
38
40
|
name: "provider",
|
|
39
41
|
type: z.string(),
|
|
40
42
|
description: "name of the provider to retrieve states for",
|
|
41
|
-
required: true
|
|
42
|
-
}
|
|
43
|
+
required: true,
|
|
44
|
+
},
|
|
43
45
|
],
|
|
44
46
|
handler: "getProviderStates",
|
|
45
|
-
clients: ["pactflow", "pact_broker"]
|
|
47
|
+
clients: ["pactflow", "pact_broker"],
|
|
46
48
|
},
|
|
47
49
|
{
|
|
48
50
|
title: "Can I Deploy",
|
|
@@ -50,6 +52,36 @@ export const TOOLS = [
|
|
|
50
52
|
purpose: "To serve as a deployment safety check within the PactBroker and PactFlow ecosystem, leveraging contract testing results to validate whether a specific service / pacticipant version is compatible with all integrated services. This feature prevents unsafe releases, reduces integration risks, and enables teams to confidently automate deployments across environments with a clear, auditable record of verification results.",
|
|
51
53
|
zodSchema: CanIDeploySchema,
|
|
52
54
|
handler: "canIDeploy",
|
|
53
|
-
clients: ["pactflow", "pact_broker"]
|
|
54
|
-
}
|
|
55
|
+
clients: ["pactflow", "pact_broker"],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
title: "Matrix",
|
|
59
|
+
summary: "Retrieve the comprehensive contract verification matrix that shows the relationship between consumer and provider versions, their associated pact files, and verification results stored in the Pact Broker or Pactflow. The matrix provides detailed visibility into which consumer and provider versions have been successfully verified against each other, and highlights failures with detailed information about the cause.",
|
|
60
|
+
purpose: "The Matrix serves as a powerful tool for teams to understand the state of their contract testing ecosystem. It enables tracking of all interactions between consumer and provider versions over time, with detailed insights into verification successes and failures. This helps teams rapidly identify compatibility issues, understand why specific verifications failed, and make informed decisions about deployments. Matrix offers a more intuitive and consolidated view of the verification status, making it easier to spot trends or problematic versions. Additionally, the Matrix supports complex queries using selectors, and can answer specific 'can-i-deploy' questions, ensuring that only compatible versions are deployed to production environments.",
|
|
61
|
+
useCases: [
|
|
62
|
+
"Quickly identify which consumer and provider version combinations have passed or failed verification.",
|
|
63
|
+
"Diagnose and investigate why a particular consumer-provider verification failed.",
|
|
64
|
+
"Visualize the overall contract compatibility across two pacticipants / services.",
|
|
65
|
+
"Perform advanced queries using selectors to understand compatibility within specific branches, environments, or version ranges.",
|
|
66
|
+
"Support informed deployment decisions by answering 'can I deploy version X of this service to production?'",
|
|
67
|
+
"Expose contract verification details to non-frequent API users in a more accessible format.",
|
|
68
|
+
],
|
|
69
|
+
zodSchema: MatrixSchema,
|
|
70
|
+
handler: "getMatrix",
|
|
71
|
+
clients: ["pactflow", "pact_broker"],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
title: "PactFlow AI Status",
|
|
75
|
+
summary: "Check PactFlow AI usage status, remaining credits, and eligibility",
|
|
76
|
+
purpose: "Retrieve the AI feature status for the PactFlow account, including whether AI is enabled, the number of remaining and consumed AI credits, and entitlement or permission issues preventing usage.",
|
|
77
|
+
useCases: [
|
|
78
|
+
"Verify if AI functionality is enabled for the account before attempting to use AI-powered features",
|
|
79
|
+
"Monitor remaining and consumed AI credits to manage usage and avoid unexpected disruptions",
|
|
80
|
+
"Detect entitlement or permission issues when a user tries to access AI features and guide corrective actions",
|
|
81
|
+
"Integrate into deployment pipelines to ensure the environment is correctly configured with necessary entitlements and sufficient credits before executing AI-driven tasks",
|
|
82
|
+
"Fetches usage and entitlement reports for auditing, budgeting, and compliance purposes",
|
|
83
|
+
],
|
|
84
|
+
handler: "getAIStatus",
|
|
85
|
+
clients: ["pactflow"],
|
|
86
|
+
},
|
|
55
87
|
];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { RemoteOpenAPIDocumentSchema, } from "./ai.js";
|
|
2
1
|
import yaml from "js-yaml";
|
|
3
2
|
// @ts-expect-error missing type declarations
|
|
4
3
|
import Swagger from "swagger-client";
|
|
4
|
+
import { RemoteOpenAPIDocumentSchema, } from "./ai.js";
|
|
5
5
|
/**
|
|
6
6
|
* Resolve the OpenAPI specification from the provided input.
|
|
7
7
|
*
|