@smartbear/mcp 0.8.0 → 0.10.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 +29 -4
- package/dist/api-hub/client/api.js +239 -10
- package/dist/api-hub/client/configuration.js +5 -0
- package/dist/api-hub/client/index.js +1 -0
- package/dist/api-hub/client/portal-types.js +126 -0
- package/dist/api-hub/client/registry-types.js +8 -0
- package/dist/api-hub/client/tools.js +78 -15
- package/dist/api-hub/client/user-management-types.js +24 -0
- package/dist/api-hub/client.js +34 -0
- package/dist/bugsnag/client/api/CurrentUser.js +12 -49
- package/dist/bugsnag/client/api/Error.js +29 -142
- package/dist/bugsnag/client/api/Project.js +52 -113
- package/dist/bugsnag/client/api/api.js +3743 -0
- package/dist/bugsnag/client/api/base.js +97 -34
- package/dist/bugsnag/client/api/configuration.js +26 -0
- package/dist/bugsnag/client/api/index.js +2 -0
- package/dist/bugsnag/client/filters.js +28 -0
- package/dist/bugsnag/client.js +104 -155
- package/dist/collaborator/client.js +364 -0
- package/dist/common/server.js +73 -23
- package/dist/common/types.js +6 -1
- package/dist/index.js +9 -1
- package/dist/pactflow/client/prompt-utils.js +2 -1
- package/dist/pactflow/client/tools.js +4 -4
- package/dist/pactflow/client/utils.js +5 -4
- package/dist/pactflow/client.js +10 -9
- package/dist/qmetry/client/api/client-api.js +21 -16
- package/dist/qmetry/client/api/error-handler.js +329 -0
- package/dist/qmetry/client/auto-resolve.js +96 -0
- package/dist/qmetry/client/handlers.js +33 -2
- package/dist/qmetry/client/issues.js +123 -0
- package/dist/qmetry/client/project.js +73 -0
- package/dist/qmetry/client/requirement.js +76 -0
- package/dist/qmetry/client/testcase.js +122 -6
- package/dist/qmetry/client/testsuite.js +272 -0
- package/dist/qmetry/client/tools/index.js +17 -0
- package/dist/qmetry/client/tools/issue-tools.js +545 -0
- package/dist/qmetry/client/tools/project-tools.js +348 -0
- package/dist/qmetry/client/tools/requirement-tools.js +530 -0
- package/dist/qmetry/client/tools/testcase-tools.js +526 -0
- package/dist/qmetry/client/tools/testsuite-tools.js +772 -0
- package/dist/qmetry/client/tools/types.js +1 -0
- package/dist/qmetry/client/utils.js +16 -0
- package/dist/qmetry/client.js +27 -17
- package/dist/qmetry/config/constants.js +28 -0
- package/dist/qmetry/config/rest-endpoints.js +30 -0
- package/dist/qmetry/types/common.js +599 -9
- package/dist/qmetry/types/issues.js +16 -0
- package/dist/qmetry/types/project.js +17 -0
- package/dist/qmetry/types/requirements.js +19 -0
- package/dist/qmetry/types/testcase.js +20 -0
- package/dist/qmetry/types/testsuite.js +44 -0
- package/dist/reflect/client.js +7 -6
- package/dist/zephyr/client.js +7 -1
- package/dist/zephyr/common/api-client.js +8 -0
- package/dist/zephyr/common/auth-service.js +1 -0
- package/dist/zephyr/common/rest-api-schemas.js +5173 -0
- package/dist/zephyr/tool/project/get-project.js +39 -0
- package/dist/zephyr/tool/project/get-projects.js +7 -13
- package/dist/zephyr/tool/test-cycle/get-test-cycles.js +72 -0
- package/package.json +1 -1
- package/dist/bugsnag/client/api/filters.js +0 -167
- package/dist/bugsnag/client/configuration.js +0 -10
- package/dist/bugsnag/client/index.js +0 -2
- package/dist/qmetry/client/tools.js +0 -222
- package/dist/zephyr/common/types.js +0 -35
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export class CollaboratorClient {
|
|
3
|
+
name = "Collaborator";
|
|
4
|
+
prefix = "collaborator";
|
|
5
|
+
baseUrl;
|
|
6
|
+
username;
|
|
7
|
+
loginTicket;
|
|
8
|
+
constructor(baseUrl, username, loginTicket) {
|
|
9
|
+
this.baseUrl = baseUrl;
|
|
10
|
+
this.username = username;
|
|
11
|
+
this.loginTicket = loginTicket;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Calls the Collaborator API with the given commands, prepending authentication automatically.
|
|
15
|
+
* @param commands Array of Collaborator API commands (excluding authentication)
|
|
16
|
+
* @returns Raw Collaborator API response
|
|
17
|
+
*/
|
|
18
|
+
async call(commands) {
|
|
19
|
+
const url = `${this.baseUrl}/services/json/v1`;
|
|
20
|
+
// Always prepend authentication command automatically
|
|
21
|
+
const body = [
|
|
22
|
+
{
|
|
23
|
+
command: "SessionService.authenticate",
|
|
24
|
+
args: { login: this.username, ticket: this.loginTicket },
|
|
25
|
+
},
|
|
26
|
+
...commands,
|
|
27
|
+
];
|
|
28
|
+
const response = await fetch(url, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: { "Content-Type": "application/json" },
|
|
31
|
+
body: JSON.stringify(body),
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`Collaborator API call failed: ${response.status} - ${await response.text()}`);
|
|
35
|
+
}
|
|
36
|
+
return await response.json();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Registers the Collaborator API tool with the MCP server. Accepts commands (excluding authentication).
|
|
40
|
+
*/
|
|
41
|
+
registerTools(register, _getInput) {
|
|
42
|
+
// findReviewById tool
|
|
43
|
+
register({
|
|
44
|
+
title: "Find Collaborator Review By ID",
|
|
45
|
+
summary: "Finds a review in Collaborator by its review ID.",
|
|
46
|
+
inputSchema: z.object({
|
|
47
|
+
reviewId: z.string().describe("The Collaborator review ID to find."),
|
|
48
|
+
}),
|
|
49
|
+
}, async (args, _extra) => {
|
|
50
|
+
const { reviewId } = args;
|
|
51
|
+
const commands = [
|
|
52
|
+
{
|
|
53
|
+
command: "ReviewService.findReviewById",
|
|
54
|
+
args: { reviewId },
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
const result = await this.call(commands);
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
// createReview tool
|
|
63
|
+
register({
|
|
64
|
+
title: "Create Collaborator Review",
|
|
65
|
+
summary: "Creates a new review in Collaborator. All parameters are optional.",
|
|
66
|
+
inputSchema: z.object({
|
|
67
|
+
creator: z
|
|
68
|
+
.string()
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("Collaborator username of the review creator. Optional. Default: currently logged in user."),
|
|
71
|
+
title: z
|
|
72
|
+
.string()
|
|
73
|
+
.optional()
|
|
74
|
+
.describe("Title of the review. Optional. Default: null."),
|
|
75
|
+
templateName: z
|
|
76
|
+
.string()
|
|
77
|
+
.optional()
|
|
78
|
+
.describe("Review template name. Optional. Default: system default template."),
|
|
79
|
+
accessPolicy: z
|
|
80
|
+
.string()
|
|
81
|
+
.optional()
|
|
82
|
+
.describe("Access policy for the review. Optional. Default: ANYONE."),
|
|
83
|
+
}),
|
|
84
|
+
}, async (args, _extra) => {
|
|
85
|
+
const commandArgs = {};
|
|
86
|
+
if (args.creator !== undefined)
|
|
87
|
+
commandArgs.creator = args.creator;
|
|
88
|
+
if (args.title !== undefined)
|
|
89
|
+
commandArgs.title = args.title;
|
|
90
|
+
if (args.templateName !== undefined)
|
|
91
|
+
commandArgs.templateName = args.templateName;
|
|
92
|
+
if (args.accessPolicy !== undefined)
|
|
93
|
+
commandArgs.accessPolicy = args.accessPolicy;
|
|
94
|
+
const commands = [
|
|
95
|
+
{
|
|
96
|
+
command: "ReviewService.createReview",
|
|
97
|
+
args: commandArgs,
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
const result = await this.call(commands);
|
|
101
|
+
return {
|
|
102
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
// rejectReview tool
|
|
106
|
+
register({
|
|
107
|
+
title: "Reject Collaborator Review",
|
|
108
|
+
summary: "Rejects a review in Collaborator by its review ID and reason.",
|
|
109
|
+
inputSchema: z.object({
|
|
110
|
+
reviewId: z
|
|
111
|
+
.union([z.string(), z.number()])
|
|
112
|
+
.describe("The Collaborator review ID to reject."),
|
|
113
|
+
reason: z.string().describe("Reason for rejecting the review."),
|
|
114
|
+
}),
|
|
115
|
+
}, async (args, _extra) => {
|
|
116
|
+
const { reviewId, reason } = args;
|
|
117
|
+
const commands = [
|
|
118
|
+
{
|
|
119
|
+
command: "ReviewService.reject",
|
|
120
|
+
args: {
|
|
121
|
+
reviewId,
|
|
122
|
+
reason,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
const result = await this.call(commands);
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
register({
|
|
132
|
+
title: "ReviewService Action",
|
|
133
|
+
summary: "Invoke any ReviewService method by name and arguments. For finishReviewPhase and waitOnPhase, provide reviewId (required) and until (optional, defaults to 'ANY').",
|
|
134
|
+
inputSchema: z.object({
|
|
135
|
+
action: z.enum([
|
|
136
|
+
"moveReviewToAnnotatePhase",
|
|
137
|
+
"cancel",
|
|
138
|
+
"reopen",
|
|
139
|
+
"uncancel",
|
|
140
|
+
]),
|
|
141
|
+
args: z.record(z.any()),
|
|
142
|
+
}),
|
|
143
|
+
}, async (params, _extra) => {
|
|
144
|
+
const { action, args } = params;
|
|
145
|
+
const commands = [{ command: `ReviewService.${action}`, args }];
|
|
146
|
+
const result = await this.call(commands);
|
|
147
|
+
return {
|
|
148
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
// getReviews tool
|
|
152
|
+
register({
|
|
153
|
+
title: "Get Collaborator Reviews",
|
|
154
|
+
summary: "Retrieves reviews from Collaborator using ReviewService.getReviews. All parameters are optional and only provided ones are sent.",
|
|
155
|
+
inputSchema: z.object({
|
|
156
|
+
login: z
|
|
157
|
+
.string()
|
|
158
|
+
.optional()
|
|
159
|
+
.describe("Collaborator username to filter reviews."),
|
|
160
|
+
role: z
|
|
161
|
+
.string()
|
|
162
|
+
.optional()
|
|
163
|
+
.describe("Role to filter reviews (e.g., AUTHOR)."),
|
|
164
|
+
creator: z
|
|
165
|
+
.boolean()
|
|
166
|
+
.optional()
|
|
167
|
+
.describe("Whether to filter by creator."),
|
|
168
|
+
reviewPhase: z
|
|
169
|
+
.string()
|
|
170
|
+
.optional()
|
|
171
|
+
.describe("Review phase to filter (e.g., PLANNING)."),
|
|
172
|
+
fullInfo: z
|
|
173
|
+
.boolean()
|
|
174
|
+
.optional()
|
|
175
|
+
.describe("Whether to retrieve full review info."),
|
|
176
|
+
fromDate: z
|
|
177
|
+
.string()
|
|
178
|
+
.optional()
|
|
179
|
+
.describe('Minimal creation date in format "yyyy-MM-dd"'),
|
|
180
|
+
toDate: z
|
|
181
|
+
.string()
|
|
182
|
+
.optional()
|
|
183
|
+
.describe('Maximal creation date in format "yyyy-MM-dd"'),
|
|
184
|
+
}),
|
|
185
|
+
}, async (args, _extra) => {
|
|
186
|
+
const reviewArgs = {};
|
|
187
|
+
if (args.login !== undefined)
|
|
188
|
+
reviewArgs.login = args.login;
|
|
189
|
+
if (args.role !== undefined)
|
|
190
|
+
reviewArgs.role = args.role;
|
|
191
|
+
if (args.creator !== undefined)
|
|
192
|
+
reviewArgs.creator = args.creator;
|
|
193
|
+
if (args.reviewPhase !== undefined)
|
|
194
|
+
reviewArgs.reviewPhase = args.reviewPhase;
|
|
195
|
+
if (args.fullInfo !== undefined)
|
|
196
|
+
reviewArgs.fullInfo = args.fullInfo;
|
|
197
|
+
if (args.fromDate !== undefined)
|
|
198
|
+
reviewArgs.fromDate = args.fromDate;
|
|
199
|
+
if (args.toDate !== undefined)
|
|
200
|
+
reviewArgs.toDate = args.toDate;
|
|
201
|
+
const commands = [
|
|
202
|
+
{
|
|
203
|
+
command: "ReviewService.getReviews",
|
|
204
|
+
args: reviewArgs,
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
const result = await this.call(commands);
|
|
208
|
+
return {
|
|
209
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
// createIntegration tool
|
|
213
|
+
register({
|
|
214
|
+
title: "Create Collaborator Remote System Configuration",
|
|
215
|
+
summary: "Creates a remote system configuration in Collaborator (e.g., Bitbucket, GitHub, etc).",
|
|
216
|
+
inputSchema: z.object({
|
|
217
|
+
token: z
|
|
218
|
+
.string()
|
|
219
|
+
.describe("Remote system token, e.g., BITBUCKET, GITHUB, etc."),
|
|
220
|
+
title: z.string().describe("Remote system title."),
|
|
221
|
+
config: z
|
|
222
|
+
.string()
|
|
223
|
+
.describe("JSON string containing configuration parameters for the remote system."),
|
|
224
|
+
reviewTemplateId: z
|
|
225
|
+
.string()
|
|
226
|
+
.optional()
|
|
227
|
+
.describe("Optional review template ID used by this remote system."),
|
|
228
|
+
}),
|
|
229
|
+
}, async (args, _extra) => {
|
|
230
|
+
const { token, title, config, reviewTemplateId } = args;
|
|
231
|
+
const commandArgs = { token, title, config };
|
|
232
|
+
if (reviewTemplateId)
|
|
233
|
+
commandArgs.reviewTemplateId = reviewTemplateId;
|
|
234
|
+
const commands = [
|
|
235
|
+
{
|
|
236
|
+
command: "AdminRemoteSystemService.createIntegration",
|
|
237
|
+
args: commandArgs,
|
|
238
|
+
},
|
|
239
|
+
];
|
|
240
|
+
const result = await this.call(commands);
|
|
241
|
+
return {
|
|
242
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
// editIntegration tool
|
|
246
|
+
register({
|
|
247
|
+
title: "Edit Collaborator Remote System Configuration",
|
|
248
|
+
summary: "Edits parameters of an existing remote system configuration in Collaborator. Only title and config are editable after creation.",
|
|
249
|
+
inputSchema: z.object({
|
|
250
|
+
id: z
|
|
251
|
+
.string()
|
|
252
|
+
.describe("ID of the remote system Configuration to edit."),
|
|
253
|
+
title: z.string().optional().describe("Remote system title."),
|
|
254
|
+
config: z
|
|
255
|
+
.string()
|
|
256
|
+
.optional()
|
|
257
|
+
.describe("JSON string containing configuration parameters for the remote system."),
|
|
258
|
+
reviewTemplateId: z
|
|
259
|
+
.string()
|
|
260
|
+
.optional()
|
|
261
|
+
.describe("Optional review template ID used by this remote system."),
|
|
262
|
+
}),
|
|
263
|
+
}, async (args, _extra) => {
|
|
264
|
+
const { id, title, config, reviewTemplateId } = args;
|
|
265
|
+
const commandArgs = { id };
|
|
266
|
+
if (title)
|
|
267
|
+
commandArgs.title = title;
|
|
268
|
+
if (config)
|
|
269
|
+
commandArgs.config = config;
|
|
270
|
+
if (reviewTemplateId)
|
|
271
|
+
commandArgs.reviewTemplateId = reviewTemplateId;
|
|
272
|
+
const commands = [
|
|
273
|
+
{
|
|
274
|
+
command: "AdminRemoteSystemService.editIntegration",
|
|
275
|
+
args: commandArgs,
|
|
276
|
+
},
|
|
277
|
+
];
|
|
278
|
+
const result = await this.call(commands);
|
|
279
|
+
return {
|
|
280
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
// deleteIntegration tool
|
|
284
|
+
register({
|
|
285
|
+
title: "Delete Collaborator Remote System Configuration",
|
|
286
|
+
summary: "Deletes a remote system configuration in Collaborator by its ID.",
|
|
287
|
+
inputSchema: z.object({
|
|
288
|
+
id: z
|
|
289
|
+
.union([z.string(), z.number()])
|
|
290
|
+
.describe("ID of the remote system Configuration to delete."),
|
|
291
|
+
}),
|
|
292
|
+
}, async (args, _extra) => {
|
|
293
|
+
const commandArgs = {};
|
|
294
|
+
if (args.id !== undefined)
|
|
295
|
+
commandArgs.id =
|
|
296
|
+
typeof args.id === "string" && !Number.isNaN(Number(args.id))
|
|
297
|
+
? Number(args.id)
|
|
298
|
+
: args.id;
|
|
299
|
+
const commands = [
|
|
300
|
+
{
|
|
301
|
+
command: "AdminRemoteSystemService.deleteIntegration",
|
|
302
|
+
args: commandArgs,
|
|
303
|
+
},
|
|
304
|
+
];
|
|
305
|
+
const result = await this.call(commands);
|
|
306
|
+
return {
|
|
307
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
register({
|
|
311
|
+
title: "Update Collaborator Remote System Configuration Webhook",
|
|
312
|
+
summary: "Updates the webhook for a remote system configuration in Collaborator by its ID.",
|
|
313
|
+
inputSchema: z.object({
|
|
314
|
+
id: z
|
|
315
|
+
.union([z.string(), z.number()])
|
|
316
|
+
.describe("ID of the remote system Configuration to update the webhook for."),
|
|
317
|
+
}),
|
|
318
|
+
}, async (args, _extra) => {
|
|
319
|
+
const commandArgs = {};
|
|
320
|
+
if (args.id !== undefined)
|
|
321
|
+
commandArgs.id =
|
|
322
|
+
typeof args.id === "string" && !Number.isNaN(Number(args.id))
|
|
323
|
+
? Number(args.id)
|
|
324
|
+
: args.id;
|
|
325
|
+
const commands = [
|
|
326
|
+
{
|
|
327
|
+
command: "AdminRemoteSystemService.updateWebhook",
|
|
328
|
+
args: commandArgs,
|
|
329
|
+
},
|
|
330
|
+
];
|
|
331
|
+
const result = await this.call(commands);
|
|
332
|
+
return {
|
|
333
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
334
|
+
};
|
|
335
|
+
});
|
|
336
|
+
// Test connection tool
|
|
337
|
+
register({
|
|
338
|
+
title: "Test Collaborator Remote System Configuration Connection",
|
|
339
|
+
summary: "Tests the connection for a remote system configuration in Collaborator by its ID.",
|
|
340
|
+
inputSchema: z.object({
|
|
341
|
+
id: z
|
|
342
|
+
.union([z.string(), z.number()])
|
|
343
|
+
.describe("ID of the remote system Configuration to test connection for."),
|
|
344
|
+
}),
|
|
345
|
+
}, async (args, _extra) => {
|
|
346
|
+
const commandArgs = {};
|
|
347
|
+
if (args.id !== undefined)
|
|
348
|
+
commandArgs.id =
|
|
349
|
+
typeof args.id === "string" && !Number.isNaN(Number(args.id))
|
|
350
|
+
? Number(args.id)
|
|
351
|
+
: args.id;
|
|
352
|
+
const commands = [
|
|
353
|
+
{
|
|
354
|
+
command: "AdminRemoteSystemService.testConnection",
|
|
355
|
+
args: commandArgs,
|
|
356
|
+
},
|
|
357
|
+
];
|
|
358
|
+
const result = await this.call(commands);
|
|
359
|
+
return {
|
|
360
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
361
|
+
};
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
package/dist/common/server.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { McpServer, ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { ZodAny, ZodArray, ZodBoolean, ZodEnum, ZodLiteral, ZodNumber, ZodObject, ZodOptional, ZodString, ZodUnion, } from "zod";
|
|
2
|
+
import { ZodAny, ZodArray, ZodBoolean, ZodEnum, ZodIntersection, ZodLiteral, ZodNumber, ZodObject, ZodOptional, ZodString, ZodUnion, } from "zod";
|
|
3
3
|
import Bugsnag from "../common/bugsnag.js";
|
|
4
4
|
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "./info.js";
|
|
5
|
+
import { ToolError } from "./types.js";
|
|
5
6
|
export class SmartBearMcpServer extends McpServer {
|
|
6
7
|
constructor() {
|
|
7
8
|
super({
|
|
@@ -26,13 +27,36 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
26
27
|
title: toolTitle,
|
|
27
28
|
description: this.getDescription(params),
|
|
28
29
|
inputSchema: this.getInputSchema(params),
|
|
30
|
+
outputSchema: this.getOutputSchema(params),
|
|
29
31
|
annotations: this.getAnnotations(toolTitle, params),
|
|
30
32
|
}, async (args, extra) => {
|
|
31
33
|
try {
|
|
32
|
-
|
|
34
|
+
const result = await cb(args, extra);
|
|
35
|
+
if (result) {
|
|
36
|
+
this.validateCallbackResult(result, params);
|
|
37
|
+
this.addStructuredContentAsText(result);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
33
40
|
}
|
|
34
41
|
catch (e) {
|
|
35
|
-
|
|
42
|
+
// ToolErrors should not be reported to BugSnag
|
|
43
|
+
if (e instanceof ToolError) {
|
|
44
|
+
return {
|
|
45
|
+
isError: true,
|
|
46
|
+
content: [
|
|
47
|
+
{
|
|
48
|
+
type: "text",
|
|
49
|
+
text: `Error executing ${toolTitle}: ${e.message}`,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
Bugsnag.notify(e, (event) => {
|
|
56
|
+
event.addMetadata("app", { tool: toolName });
|
|
57
|
+
event.unhandled = true;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
36
60
|
throw e;
|
|
37
61
|
}
|
|
38
62
|
});
|
|
@@ -41,14 +65,18 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
41
65
|
});
|
|
42
66
|
if (client.registerResources) {
|
|
43
67
|
client.registerResources((name, path, cb) => {
|
|
44
|
-
|
|
68
|
+
const url = `${client.prefix}://${name}/${path}`;
|
|
69
|
+
return super.registerResource(name, new ResourceTemplate(url, {
|
|
45
70
|
list: undefined,
|
|
46
71
|
}), {}, async (url, variables, extra) => {
|
|
47
72
|
try {
|
|
48
73
|
return await cb(url, variables, extra);
|
|
49
74
|
}
|
|
50
75
|
catch (e) {
|
|
51
|
-
Bugsnag.notify(e)
|
|
76
|
+
Bugsnag.notify(e, (event) => {
|
|
77
|
+
event.addMetadata("app", { resource: name, url: url });
|
|
78
|
+
event.unhandled = true;
|
|
79
|
+
});
|
|
52
80
|
throw e;
|
|
53
81
|
}
|
|
54
82
|
});
|
|
@@ -60,6 +88,24 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
60
88
|
});
|
|
61
89
|
}
|
|
62
90
|
}
|
|
91
|
+
validateCallbackResult(result, params) {
|
|
92
|
+
if (result.isError) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (params.outputSchema && !result.structuredContent) {
|
|
96
|
+
throw new Error(`The result of the tool '${params.title}' must include 'structuredContent'`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
addStructuredContentAsText(result) {
|
|
100
|
+
if (result.structuredContent && !result.content?.length) {
|
|
101
|
+
result.content = [
|
|
102
|
+
{
|
|
103
|
+
type: "text",
|
|
104
|
+
text: JSON.stringify(result.structuredContent),
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
63
109
|
getAnnotations(toolTitle, params) {
|
|
64
110
|
const annotations = {
|
|
65
111
|
title: toolTitle,
|
|
@@ -81,24 +127,28 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
81
127
|
args[param.name] = args[param.name].optional();
|
|
82
128
|
}
|
|
83
129
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
130
|
+
return { ...args, ...this.schemaToRawShape(params.inputSchema) };
|
|
131
|
+
}
|
|
132
|
+
schemaToRawShape(schema) {
|
|
133
|
+
if (schema) {
|
|
134
|
+
if (schema instanceof ZodObject) {
|
|
135
|
+
return schema.shape;
|
|
136
|
+
}
|
|
137
|
+
if (schema instanceof ZodIntersection) {
|
|
138
|
+
const leftShape = this.schemaToRawShape(schema._def.left);
|
|
139
|
+
const rightShape = this.schemaToRawShape(schema._def.right);
|
|
140
|
+
return { ...leftShape, ...rightShape };
|
|
94
141
|
}
|
|
95
142
|
}
|
|
96
|
-
return
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
getOutputSchema(params) {
|
|
146
|
+
return this.schemaToRawShape(params.outputSchema);
|
|
97
147
|
}
|
|
98
148
|
getDescription(params) {
|
|
99
|
-
const { summary, useCases, examples, parameters,
|
|
149
|
+
const { summary, useCases, examples, parameters, inputSchema, hints, outputDescription, } = params;
|
|
100
150
|
let description = summary;
|
|
101
|
-
// Parameters if available otherwise use
|
|
151
|
+
// Parameters if available otherwise use inputSchema
|
|
102
152
|
if ((parameters ?? []).length > 0) {
|
|
103
153
|
description += `\n\n**Parameters:**\n${parameters
|
|
104
154
|
?.map((p) => `- ${p.name} (${this.getReadableTypeName(p.type)})${p.required ? " *required*" : ""}` +
|
|
@@ -107,14 +157,14 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
107
157
|
`${p.constraints ? `\n - ${p.constraints.join("\n - ")}` : ""}`)
|
|
108
158
|
.join("\n")}`;
|
|
109
159
|
}
|
|
110
|
-
if (
|
|
160
|
+
if (inputSchema && inputSchema instanceof ZodObject) {
|
|
111
161
|
description += "\n\n**Parameters:**\n";
|
|
112
|
-
description += Object.keys(
|
|
113
|
-
.map((key) => this.formatParameterDescription(key,
|
|
162
|
+
description += Object.keys(inputSchema.shape)
|
|
163
|
+
.map((key) => this.formatParameterDescription(key, inputSchema.shape[key]))
|
|
114
164
|
.join("\n");
|
|
115
165
|
}
|
|
116
|
-
if (
|
|
117
|
-
description += `\n\n**Output
|
|
166
|
+
if (outputDescription) {
|
|
167
|
+
description += `\n\n**Output Description:** ${outputDescription}`;
|
|
118
168
|
}
|
|
119
169
|
// Use Cases
|
|
120
170
|
if (useCases && useCases.length > 0) {
|
package/dist/common/types.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { ApiHubClient } from "./api-hub/client.js";
|
|
4
4
|
import { BugsnagClient } from "./bugsnag/client.js";
|
|
5
|
+
import { CollaboratorClient } from "./collaborator/client.js";
|
|
5
6
|
import Bugsnag from "./common/bugsnag.js";
|
|
6
7
|
import { SmartBearMcpServer } from "./common/server.js";
|
|
7
8
|
import { PactflowClient } from "./pactflow/client.js";
|
|
@@ -27,6 +28,9 @@ async function main() {
|
|
|
27
28
|
const qmetryBaseUrl = process.env.QMETRY_BASE_URL;
|
|
28
29
|
const zephyrToken = process.env.ZEPHYR_API_TOKEN;
|
|
29
30
|
const zephyrBaseUrl = process.env.ZEPHYR_BASE_URL;
|
|
31
|
+
const collaboratorBaseUrl = process.env.COLLAB_BASE_URL;
|
|
32
|
+
const collaboratorUsername = process.env.COLLAB_USERNAME;
|
|
33
|
+
const collaboratorLoginTicket = process.env.COLLAB_LOGIN_TICKET;
|
|
30
34
|
let client_defined = false;
|
|
31
35
|
if (reflectToken) {
|
|
32
36
|
server.addClient(new ReflectClient(reflectToken));
|
|
@@ -63,8 +67,12 @@ async function main() {
|
|
|
63
67
|
server.addClient(new ZephyrClient(zephyrToken, zephyrBaseUrl));
|
|
64
68
|
client_defined = true;
|
|
65
69
|
}
|
|
70
|
+
if (collaboratorBaseUrl && collaboratorUsername && collaboratorLoginTicket) {
|
|
71
|
+
server.addClient(new CollaboratorClient(collaboratorBaseUrl, collaboratorUsername, collaboratorLoginTicket));
|
|
72
|
+
client_defined = true;
|
|
73
|
+
}
|
|
66
74
|
if (!client_defined) {
|
|
67
|
-
console.error("
|
|
75
|
+
console.error("No SmartBear products were configured. Please provide at least one of the required configuration options.");
|
|
68
76
|
process.exit(1);
|
|
69
77
|
}
|
|
70
78
|
const transport = new StdioServerTransport();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ToolError } from "../../common/types.js";
|
|
1
2
|
import { EndpointMatcherSchema, MatcherRecommendationInputSchema, } from "./ai.js";
|
|
2
3
|
import { OADMatcherPrompt } from "./prompts.js";
|
|
3
4
|
/**
|
|
@@ -30,7 +31,7 @@ export async function getOADMatcherRecommendations(openAPI, server) {
|
|
|
30
31
|
return matcherRecommendations;
|
|
31
32
|
}
|
|
32
33
|
else {
|
|
33
|
-
throw new
|
|
34
|
+
throw new ToolError("Unable to parse recommendations please provide OpenAPI matchers manually.");
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
@@ -17,7 +17,7 @@ export const TOOLS = [
|
|
|
17
17
|
title: "Generate Pact Tests",
|
|
18
18
|
summary: "Generate Pact tests using PactFlow AI. You can provide one or more of the following input types: (1) request/response pairs for specific interactions, (2) code files to analyze and extract interactions from, and/or (3) OpenAPI document to generate tests for specific endpoints. When providing an OpenAPI document, a matcher is required to specify which endpoints to generate tests for.",
|
|
19
19
|
purpose: "Generate Pact tests for API interactions",
|
|
20
|
-
|
|
20
|
+
inputSchema: GenerationInputSchema,
|
|
21
21
|
handler: "generate",
|
|
22
22
|
clients: ["pactflow"], // ONLY pactflow
|
|
23
23
|
enableElicitation: true,
|
|
@@ -26,7 +26,7 @@ export const TOOLS = [
|
|
|
26
26
|
title: "Review Pact Tests",
|
|
27
27
|
summary: "Review Pact tests using PactFlow AI. You can provide the following inputs: (1) Pact tests to be reviewed along with metadata",
|
|
28
28
|
purpose: "Review Pact tests for API interactions",
|
|
29
|
-
|
|
29
|
+
inputSchema: RefineInputSchema,
|
|
30
30
|
handler: "review",
|
|
31
31
|
clients: ["pactflow"],
|
|
32
32
|
enableElicitation: true,
|
|
@@ -50,7 +50,7 @@ export const TOOLS = [
|
|
|
50
50
|
title: "Can I Deploy",
|
|
51
51
|
summary: "Performs a comprehensive compatibility check to determine whether a specific version of a service (pacticipant) can be safely deployed into a given environment. It analyzes the complete contract matrix of consumer-provider relationships to confirm that all required integrations are verified and compatible.",
|
|
52
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.",
|
|
53
|
-
|
|
53
|
+
inputSchema: CanIDeploySchema,
|
|
54
54
|
handler: "canIDeploy",
|
|
55
55
|
clients: ["pactflow", "pact_broker"],
|
|
56
56
|
},
|
|
@@ -66,7 +66,7 @@ export const TOOLS = [
|
|
|
66
66
|
"Support informed deployment decisions by answering 'can I deploy version X of this service to production?'",
|
|
67
67
|
"Expose contract verification details to non-frequent API users in a more accessible format.",
|
|
68
68
|
],
|
|
69
|
-
|
|
69
|
+
inputSchema: MatrixSchema,
|
|
70
70
|
handler: "getMatrix",
|
|
71
71
|
clients: ["pactflow", "pact_broker"],
|
|
72
72
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import yaml from "js-yaml";
|
|
2
2
|
// @ts-expect-error missing type declarations
|
|
3
3
|
import Swagger from "swagger-client";
|
|
4
|
+
import { ToolError } from "../../common/types.js";
|
|
4
5
|
import { RemoteOpenAPIDocumentSchema, } from "./ai.js";
|
|
5
6
|
/**
|
|
6
7
|
* Resolve the OpenAPI specification from the provided input.
|
|
@@ -12,12 +13,12 @@ import { RemoteOpenAPIDocumentSchema, } from "./ai.js";
|
|
|
12
13
|
export async function resolveOpenAPISpec(remoteOpenAPIDocument) {
|
|
13
14
|
const openAPISchema = RemoteOpenAPIDocumentSchema.safeParse(remoteOpenAPIDocument);
|
|
14
15
|
if (openAPISchema.error || !remoteOpenAPIDocument) {
|
|
15
|
-
throw new
|
|
16
|
+
throw new ToolError(`Invalid RemoteOpenAPIDocument: ${JSON.stringify(openAPISchema.error?.issues)}`);
|
|
16
17
|
}
|
|
17
18
|
const unresolvedSpec = await getRemoteSpecContents(openAPISchema.data);
|
|
18
19
|
const resolvedSpec = await Swagger.resolve({ spec: unresolvedSpec });
|
|
19
20
|
if (resolvedSpec.errors?.length) {
|
|
20
|
-
throw new
|
|
21
|
+
throw new ToolError(`Failed to resolve OpenAPI document: ${resolvedSpec.errors?.join(", ")}`);
|
|
21
22
|
}
|
|
22
23
|
return resolvedSpec.spec;
|
|
23
24
|
}
|
|
@@ -30,7 +31,7 @@ export async function resolveOpenAPISpec(remoteOpenAPIDocument) {
|
|
|
30
31
|
*/
|
|
31
32
|
export async function getRemoteSpecContents(openAPISchema) {
|
|
32
33
|
if (!openAPISchema.url) {
|
|
33
|
-
throw new
|
|
34
|
+
throw new ToolError("'url' must be provided.");
|
|
34
35
|
}
|
|
35
36
|
let headers = {};
|
|
36
37
|
if (openAPISchema.authToken) {
|
|
@@ -51,7 +52,7 @@ export async function getRemoteSpecContents(openAPISchema) {
|
|
|
51
52
|
return yaml.load(specRawBody);
|
|
52
53
|
}
|
|
53
54
|
catch (yamlError) {
|
|
54
|
-
throw new
|
|
55
|
+
throw new ToolError(`Unsupported Content-Type: ${remoteSpec.headers.get("Content-Type")} for remote OpenAPI document. Found following parse errors:-\nJSON parse error: ${jsonError}\nYAML parse error: ${yamlError}`);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
}
|