@smartbear/mcp 0.5.0 → 0.6.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 +2 -108
- package/dist/bugsnag/client/api/CurrentUser.js +2 -3
- package/dist/bugsnag/client/api/Project.js +122 -6
- package/dist/bugsnag/client/api/base.js +21 -10
- package/dist/bugsnag/client.js +431 -17
- package/dist/common/server.js +9 -0
- package/dist/index.js +2 -2
- package/dist/pactflow/client/ai.js +33 -2
- package/dist/pactflow/client/base.js +16 -3
- package/dist/pactflow/client/prompt-utils.js +89 -0
- package/dist/pactflow/client/prompts.js +133 -0
- package/dist/pactflow/client/tools.js +35 -3
- package/dist/pactflow/client.js +137 -6
- package/package.json +4 -2
package/dist/bugsnag/client.js
CHANGED
|
@@ -2,8 +2,9 @@ import NodeCache from "node-cache";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
|
|
4
4
|
import { CurrentUserAPI, ErrorAPI, Configuration } from "./client/index.js";
|
|
5
|
-
import { ProjectAPI } from "./client/api/Project.js";
|
|
6
5
|
import { FilterObjectSchema, toQueryString } from "./client/api/filters.js";
|
|
6
|
+
import { ProjectAPI, } from "./client/api/Project.js";
|
|
7
|
+
import { getNextUrlPathFromHeader } from "./client/api/base.js";
|
|
7
8
|
const HUB_PREFIX = "00000";
|
|
8
9
|
const DEFAULT_DOMAIN = "bugsnag.com";
|
|
9
10
|
const HUB_DOMAIN = "bugsnag.smartbear.com";
|
|
@@ -12,6 +13,9 @@ const cacheKeys = {
|
|
|
12
13
|
PROJECTS: "bugsnag_projects",
|
|
13
14
|
CURRENT_PROJECT: "bugsnag_current_project",
|
|
14
15
|
CURRENT_PROJECT_EVENT_FILTERS: "bugsnag_current_project_event_filters",
|
|
16
|
+
BUILD: "bugsnag_build", // + buildId
|
|
17
|
+
RELEASE: "bugsnag_release", // + releaseId
|
|
18
|
+
BUILDS_IN_RELEASE: "bugsnag_builds_in_release" // + releaseId
|
|
15
19
|
};
|
|
16
20
|
// Exclude certain event fields from the project event filters to improve agent usage
|
|
17
21
|
const EXCLUDED_EVENT_FIELDS = new Set([
|
|
@@ -33,7 +37,7 @@ export class BugsnagClient {
|
|
|
33
37
|
projectApiKey;
|
|
34
38
|
apiEndpoint;
|
|
35
39
|
appEndpoint;
|
|
36
|
-
name = "
|
|
40
|
+
name = "BugSnag";
|
|
37
41
|
prefix = "bugsnag";
|
|
38
42
|
constructor(token, projectApiKey, endpoint) {
|
|
39
43
|
this.apiEndpoint = this.getEndpoint("api", projectApiKey, endpoint);
|
|
@@ -50,14 +54,32 @@ export class BugsnagClient {
|
|
|
50
54
|
});
|
|
51
55
|
this.currentUserApi = new CurrentUserAPI(config);
|
|
52
56
|
this.errorsApi = new ErrorAPI(config);
|
|
53
|
-
this.cache = new NodeCache(
|
|
57
|
+
this.cache = new NodeCache({
|
|
58
|
+
stdTTL: 24 * 60 * 60, // default cache TTL of 24 hours
|
|
59
|
+
});
|
|
54
60
|
this.projectApi = new ProjectAPI(config);
|
|
55
61
|
this.projectApiKey = projectApiKey;
|
|
56
62
|
}
|
|
57
63
|
async initialize() {
|
|
58
64
|
// Trigger caching of org and projects
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
try {
|
|
66
|
+
await this.getProjects();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// Swallow auth errors here to allow the tools to be registered for visibility, even if the token is invalid
|
|
70
|
+
console.error("Unable to connect to BugSnag APIs, the BugSnag tools will not work. Check your configured BugSnag auth token.", error);
|
|
71
|
+
}
|
|
72
|
+
if (this.projectApiKey) {
|
|
73
|
+
try {
|
|
74
|
+
await this.getCurrentProject();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// Clear the project API key to allow tools to work across all projects
|
|
78
|
+
this.projectApiKey = undefined;
|
|
79
|
+
console.error("Unable to find your configured BugSnag project, the BugSnag tools will continue to work across all projects in your organization. " +
|
|
80
|
+
"Check your configured BugSnag project API key.", error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
61
83
|
}
|
|
62
84
|
getHost(apiKey, subdomain) {
|
|
63
85
|
if (apiKey && apiKey.startsWith(HUB_PREFIX)) {
|
|
@@ -127,10 +149,7 @@ export class BugsnagClient {
|
|
|
127
149
|
let projects = this.cache.get(cacheKeys.PROJECTS);
|
|
128
150
|
if (!projects) {
|
|
129
151
|
const org = await this.getOrganization();
|
|
130
|
-
const
|
|
131
|
-
paginate: true
|
|
132
|
-
};
|
|
133
|
-
const response = await this.currentUserApi.getOrganizationProjects(org.id, options);
|
|
152
|
+
const response = await this.currentUserApi.getOrganizationProjects(org.id);
|
|
134
153
|
projects = response.body || [];
|
|
135
154
|
this.cache.set(cacheKeys.PROJECTS, projects);
|
|
136
155
|
}
|
|
@@ -192,6 +211,83 @@ export class BugsnagClient {
|
|
|
192
211
|
return currentProject;
|
|
193
212
|
}
|
|
194
213
|
}
|
|
214
|
+
async listBuilds(projectId, opts) {
|
|
215
|
+
const response = await this.projectApi.listBuilds(projectId, opts);
|
|
216
|
+
const fetchedBuilds = response.body || [];
|
|
217
|
+
const nextUrl = getNextUrlPathFromHeader(response.headers, this.apiEndpoint);
|
|
218
|
+
const stabilityTargets = await this.getProjectStabilityTargets(projectId);
|
|
219
|
+
const formattedBuilds = fetchedBuilds.map((b) => this.addStabilityData(b, stabilityTargets));
|
|
220
|
+
return { builds: formattedBuilds, nextUrl };
|
|
221
|
+
}
|
|
222
|
+
async getBuild(projectId, buildId) {
|
|
223
|
+
const cacheKey = `${cacheKeys.BUILD}_${buildId}`;
|
|
224
|
+
const build = this.cache.get(cacheKey);
|
|
225
|
+
if (build)
|
|
226
|
+
return build;
|
|
227
|
+
const fetchedBuild = (await this.projectApi.getBuild(projectId, buildId)).body;
|
|
228
|
+
if (!fetchedBuild)
|
|
229
|
+
throw new Error(`No build for ${buildId} found.`);
|
|
230
|
+
const stabilityTargets = await this.getProjectStabilityTargets(projectId);
|
|
231
|
+
const formattedBuild = this.addStabilityData(fetchedBuild, stabilityTargets);
|
|
232
|
+
this.cache.set(cacheKey, formattedBuild, 5 * 60);
|
|
233
|
+
return formattedBuild;
|
|
234
|
+
}
|
|
235
|
+
async listReleases(projectId, opts) {
|
|
236
|
+
const response = await this.projectApi.listReleases(projectId, opts);
|
|
237
|
+
const fetchedReleases = response.body || [];
|
|
238
|
+
const nextUrl = getNextUrlPathFromHeader(response.headers, this.apiEndpoint);
|
|
239
|
+
const stabilityTargets = await this.getProjectStabilityTargets(projectId);
|
|
240
|
+
const formattedReleases = fetchedReleases.map((r) => this.addStabilityData(r, stabilityTargets));
|
|
241
|
+
return { releases: formattedReleases, nextUrl };
|
|
242
|
+
}
|
|
243
|
+
async getRelease(projectId, releaseId) {
|
|
244
|
+
const cacheKey = `${cacheKeys.RELEASE}_${releaseId}`;
|
|
245
|
+
const release = this.cache.get(cacheKey);
|
|
246
|
+
if (release)
|
|
247
|
+
return release;
|
|
248
|
+
const fetchedRelease = (await this.projectApi.getRelease(releaseId)).body;
|
|
249
|
+
if (!fetchedRelease)
|
|
250
|
+
throw new Error(`No release for ${releaseId} found.`);
|
|
251
|
+
const stabilityTargets = await this.getProjectStabilityTargets(projectId);
|
|
252
|
+
const formattedRelease = this.addStabilityData(fetchedRelease, stabilityTargets);
|
|
253
|
+
this.cache.set(cacheKey, formattedRelease, 5 * 60);
|
|
254
|
+
return formattedRelease;
|
|
255
|
+
}
|
|
256
|
+
async listBuildsInRelease(releaseId) {
|
|
257
|
+
const cacheKey = `${cacheKeys.BUILDS_IN_RELEASE}_${releaseId}`;
|
|
258
|
+
const builds = this.cache.get(cacheKey);
|
|
259
|
+
if (builds)
|
|
260
|
+
return builds;
|
|
261
|
+
const fetchedBuilds = (await this.projectApi.listBuildsInRelease(releaseId)).body || [];
|
|
262
|
+
this.cache.set(cacheKey, fetchedBuilds, 5 * 60);
|
|
263
|
+
return fetchedBuilds;
|
|
264
|
+
}
|
|
265
|
+
async getProjectStabilityTargets(projectId) {
|
|
266
|
+
return await this.projectApi.getProjectStabilityTargets(projectId);
|
|
267
|
+
}
|
|
268
|
+
addStabilityData(source, stabilityTargets) {
|
|
269
|
+
const { stability_target_type, target_stability, critical_stability } = stabilityTargets;
|
|
270
|
+
const user_stability = source.accumulative_daily_users_seen === 0 // avoid division by zero
|
|
271
|
+
? 0
|
|
272
|
+
: (source.accumulative_daily_users_seen - source.accumulative_daily_users_with_unhandled) /
|
|
273
|
+
source.accumulative_daily_users_seen;
|
|
274
|
+
const session_stability = source.total_sessions_count === 0 // avoid division by zero
|
|
275
|
+
? 0
|
|
276
|
+
: (source.total_sessions_count - source.unhandled_sessions_count) / source.total_sessions_count;
|
|
277
|
+
const stabilityMetric = stability_target_type === "user" ? user_stability : session_stability;
|
|
278
|
+
const meets_target_stability = stabilityMetric >= target_stability.value;
|
|
279
|
+
const meets_critical_stability = stabilityMetric >= critical_stability.value;
|
|
280
|
+
return {
|
|
281
|
+
...source,
|
|
282
|
+
user_stability,
|
|
283
|
+
session_stability,
|
|
284
|
+
stability_target_type,
|
|
285
|
+
target_stability: target_stability.value,
|
|
286
|
+
critical_stability: critical_stability.value,
|
|
287
|
+
meets_target_stability,
|
|
288
|
+
meets_critical_stability,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
195
291
|
registerTools(register, getInput) {
|
|
196
292
|
if (!this.projectApiKey) {
|
|
197
293
|
register({
|
|
@@ -420,7 +516,6 @@ export class BugsnagClient {
|
|
|
420
516
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
421
517
|
};
|
|
422
518
|
});
|
|
423
|
-
// Dynamically infer the filters schema from cached project event fields
|
|
424
519
|
register({
|
|
425
520
|
title: "List Project Errors",
|
|
426
521
|
summary: "List and search errors in a project using customizable filters and pagination",
|
|
@@ -434,7 +529,10 @@ export class BugsnagClient {
|
|
|
434
529
|
parameters: [
|
|
435
530
|
{
|
|
436
531
|
name: "filters",
|
|
437
|
-
type: FilterObjectSchema
|
|
532
|
+
type: FilterObjectSchema.default({
|
|
533
|
+
"event.since": [{ type: "eq", value: "30d" }],
|
|
534
|
+
"error.status": [{ type: "eq", value: "open" }]
|
|
535
|
+
}),
|
|
438
536
|
description: "Apply filters to narrow down the error list. Use the List Project Event Filters tool to discover available filter fields",
|
|
439
537
|
required: false,
|
|
440
538
|
examples: [
|
|
@@ -451,8 +549,8 @@ export class BugsnagClient {
|
|
|
451
549
|
},
|
|
452
550
|
{
|
|
453
551
|
name: "sort",
|
|
454
|
-
type: z.enum(["first_seen", "last_seen", "events", "users", "unsorted"]),
|
|
455
|
-
description: "Field to sort the errors by
|
|
552
|
+
type: z.enum(["first_seen", "last_seen", "events", "users", "unsorted"]).default("last_seen"),
|
|
553
|
+
description: "Field to sort the errors by",
|
|
456
554
|
required: false,
|
|
457
555
|
examples: ["last_seen"]
|
|
458
556
|
},
|
|
@@ -465,7 +563,7 @@ export class BugsnagClient {
|
|
|
465
563
|
},
|
|
466
564
|
{
|
|
467
565
|
name: "per_page",
|
|
468
|
-
type: z.number().min(1).max(100),
|
|
566
|
+
type: z.number().min(1).max(100).default(30),
|
|
469
567
|
description: "How many results to return per page.",
|
|
470
568
|
required: false,
|
|
471
569
|
examples: ["30", "50", "100"]
|
|
@@ -525,6 +623,7 @@ export class BugsnagClient {
|
|
|
525
623
|
"Combine multiple filters to narrow results - filters are applied with AND logic",
|
|
526
624
|
"For time filters: use relative format (7d, 24h) for recent periods or ISO 8601 UTC format (2018-05-20T00:00:00Z) for specific dates",
|
|
527
625
|
"Common time filters: event.since (from this time), event.before (until this time)",
|
|
626
|
+
"The 'event.since' filter and 'error.status' filters are always applied and if not specified are set to '30d' and 'open' respectively",
|
|
528
627
|
"There may not be any errors matching the filters - this is not a problem with the tool, in fact it might be a good thing that the user's application had no errors",
|
|
529
628
|
"This tool returns paged results. The 'count' field indicates the number of results returned in the current page, and the 'total' field indicates the total number of results across all pages.",
|
|
530
629
|
"If the output contains a 'next' value, there are more results available - call this tool again supplying the next URL as a parameter to retrieve the next page.",
|
|
@@ -542,9 +641,13 @@ export class BugsnagClient {
|
|
|
542
641
|
}
|
|
543
642
|
}
|
|
544
643
|
}
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
644
|
+
const defaultFilters = {
|
|
645
|
+
"event.since": [{ "type": "eq", "value": "30d" }],
|
|
646
|
+
"error.status": [{ "type": "eq", "value": "open" }]
|
|
647
|
+
};
|
|
648
|
+
const options = {
|
|
649
|
+
filters: { ...defaultFilters, ...args.filters }
|
|
650
|
+
};
|
|
548
651
|
if (args.sort !== undefined)
|
|
549
652
|
options.sort = args.sort;
|
|
550
653
|
if (args.direction !== undefined)
|
|
@@ -673,6 +776,317 @@ export class BugsnagClient {
|
|
|
673
776
|
content: [{ type: "text", text: JSON.stringify({ success: result }) }],
|
|
674
777
|
};
|
|
675
778
|
});
|
|
779
|
+
register({
|
|
780
|
+
title: "List Builds",
|
|
781
|
+
summary: "List builds for a project with optional filtering by release stage",
|
|
782
|
+
purpose: "Retrieve a list of build summaries to analyze deployment history and associated errors",
|
|
783
|
+
useCases: [
|
|
784
|
+
"View recent builds to correlate with error spikes",
|
|
785
|
+
"Filter builds by stage (e.g. production, staging) for targeted analysis",
|
|
786
|
+
],
|
|
787
|
+
parameters: [
|
|
788
|
+
...(this.projectApiKey
|
|
789
|
+
? []
|
|
790
|
+
: [
|
|
791
|
+
{
|
|
792
|
+
name: "projectId",
|
|
793
|
+
type: z.string(),
|
|
794
|
+
description: "ID of the project to list builds for",
|
|
795
|
+
required: true,
|
|
796
|
+
},
|
|
797
|
+
]),
|
|
798
|
+
{
|
|
799
|
+
name: "releaseStage",
|
|
800
|
+
type: z.string(),
|
|
801
|
+
description: "Filter builds by this stage (e.g. production, staging)",
|
|
802
|
+
required: false,
|
|
803
|
+
examples: ["production", "staging"],
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
name: "nextUrl",
|
|
807
|
+
type: z.string(),
|
|
808
|
+
description: "URL for retrieving the next page of results. Use the value in the previous response to get the next page when more results are available. If provided, other parameters are ignored.",
|
|
809
|
+
required: false,
|
|
810
|
+
examples: [
|
|
811
|
+
"/projects/515fb9337c1074f6fd000003/builds?offset=30&per_page=30",
|
|
812
|
+
],
|
|
813
|
+
},
|
|
814
|
+
],
|
|
815
|
+
examples: [
|
|
816
|
+
{
|
|
817
|
+
description: "List all builds for a project",
|
|
818
|
+
parameters: {},
|
|
819
|
+
expectedOutput: "JSON array of build objects with metadata",
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
description: "List production builds for a project",
|
|
823
|
+
parameters: {
|
|
824
|
+
releaseStage: "production",
|
|
825
|
+
},
|
|
826
|
+
expectedOutput: "JSON array of build objects in the production stage",
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
description: "Get the next page of results",
|
|
830
|
+
parameters: {
|
|
831
|
+
nextUrl: "/projects/515fb9337c1074f6fd000003/builds?offset=30&per_page=30",
|
|
832
|
+
},
|
|
833
|
+
expectedOutput: "JSON array of build objects with metadata from the next page",
|
|
834
|
+
}
|
|
835
|
+
],
|
|
836
|
+
hints: ["For more detailed results use the Get Build tool"],
|
|
837
|
+
readOnly: true,
|
|
838
|
+
idempotent: true,
|
|
839
|
+
outputFormat: "JSON array of build summary objects with metadata",
|
|
840
|
+
}, async (args, _extra) => {
|
|
841
|
+
const project = await this.getInputProject(args.projectId);
|
|
842
|
+
const { builds, nextUrl } = await this.listBuilds(project.id, {
|
|
843
|
+
release_stage: args.releaseStage,
|
|
844
|
+
next_url: args.nextUrl,
|
|
845
|
+
});
|
|
846
|
+
return {
|
|
847
|
+
content: [
|
|
848
|
+
{
|
|
849
|
+
type: "text",
|
|
850
|
+
text: JSON.stringify({
|
|
851
|
+
builds,
|
|
852
|
+
next: nextUrl,
|
|
853
|
+
}),
|
|
854
|
+
}
|
|
855
|
+
],
|
|
856
|
+
};
|
|
857
|
+
});
|
|
858
|
+
register({
|
|
859
|
+
title: "Get Build",
|
|
860
|
+
summary: "Get more details for a specific build by its ID",
|
|
861
|
+
purpose: "Retrieve detailed information about a build for analysis and debugging",
|
|
862
|
+
useCases: [
|
|
863
|
+
"View build metadata such as version, source control info, and error counts",
|
|
864
|
+
"Analyze a specific build to correlate with error spikes or deployments",
|
|
865
|
+
"See the stability targets for a project and if the build meets them",
|
|
866
|
+
],
|
|
867
|
+
parameters: [
|
|
868
|
+
...(this.projectApiKey
|
|
869
|
+
? []
|
|
870
|
+
: [
|
|
871
|
+
{
|
|
872
|
+
name: "projectId",
|
|
873
|
+
type: z.string(),
|
|
874
|
+
description: "ID of the project containing the build",
|
|
875
|
+
required: true,
|
|
876
|
+
},
|
|
877
|
+
]),
|
|
878
|
+
{
|
|
879
|
+
name: "buildId",
|
|
880
|
+
type: z.string(),
|
|
881
|
+
description: "ID of the build to retrieve",
|
|
882
|
+
required: true,
|
|
883
|
+
examples: ["5f8d0d55c9e77c0017a1b2c3"],
|
|
884
|
+
},
|
|
885
|
+
],
|
|
886
|
+
examples: [
|
|
887
|
+
{
|
|
888
|
+
description: "Get details for a specific build",
|
|
889
|
+
parameters: {
|
|
890
|
+
buildId: "5f8d0d55c9e77c0017a1b2c3",
|
|
891
|
+
},
|
|
892
|
+
expectedOutput: "JSON object with build details including version, source control info, error counts and stability data.",
|
|
893
|
+
},
|
|
894
|
+
],
|
|
895
|
+
hints: ["Build IDs can be found using the List builds tool"],
|
|
896
|
+
readOnly: true,
|
|
897
|
+
idempotent: true,
|
|
898
|
+
outputFormat: "JSON object containing build details along with stability metrics such as user and session stability, and whether it meets project targets",
|
|
899
|
+
}, async (args, _extra) => {
|
|
900
|
+
if (!args.buildId)
|
|
901
|
+
throw new Error("buildId argument is required");
|
|
902
|
+
const build = await this.getBuild((await this.getInputProject(args.projectId)).id, args.buildId);
|
|
903
|
+
return {
|
|
904
|
+
content: [{ type: "text", text: JSON.stringify(build) }],
|
|
905
|
+
};
|
|
906
|
+
});
|
|
907
|
+
register({
|
|
908
|
+
title: "List Releases",
|
|
909
|
+
summary: "List releases for a project with optional filtering by release stage",
|
|
910
|
+
purpose: "Retrieve a list of release summaries to analyze deployment history and associated errors",
|
|
911
|
+
useCases: [
|
|
912
|
+
"View recent releases to correlate with error spikes",
|
|
913
|
+
"Filter releases by stage (e.g. production, staging) for targeted analysis",
|
|
914
|
+
],
|
|
915
|
+
parameters: [
|
|
916
|
+
...(this.projectApiKey
|
|
917
|
+
? []
|
|
918
|
+
: [
|
|
919
|
+
{
|
|
920
|
+
name: "projectId",
|
|
921
|
+
type: z.string(),
|
|
922
|
+
description: "ID of the project to list releases for",
|
|
923
|
+
required: true,
|
|
924
|
+
},
|
|
925
|
+
]),
|
|
926
|
+
{
|
|
927
|
+
name: "releaseStage",
|
|
928
|
+
type: z.string(),
|
|
929
|
+
description: "Filter releases by this stage (e.g. production, staging)",
|
|
930
|
+
required: false,
|
|
931
|
+
examples: ["production", "staging"],
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
name: "visibleOnly",
|
|
935
|
+
type: z.boolean().default(true),
|
|
936
|
+
description: "Whether to only include releases that are marked as visible (default: true)",
|
|
937
|
+
required: true,
|
|
938
|
+
examples: ["true", "false"],
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
name: "nextUrl",
|
|
942
|
+
type: z.string(),
|
|
943
|
+
description: "URL for retrieving the next page of results. Use the value in the previous response to get the next page when more results are available. If provided, other parameters are ignored.",
|
|
944
|
+
required: false,
|
|
945
|
+
examples: [
|
|
946
|
+
"/projects/515fb9337c1074f6fd000003/releases?offset=30&per_page=30",
|
|
947
|
+
],
|
|
948
|
+
},
|
|
949
|
+
],
|
|
950
|
+
examples: [
|
|
951
|
+
{
|
|
952
|
+
description: "List all releases for a project",
|
|
953
|
+
parameters: {},
|
|
954
|
+
expectedOutput: "JSON array of release objects with metadata",
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
description: "List production releases for a project",
|
|
958
|
+
parameters: {
|
|
959
|
+
releaseStage: "production",
|
|
960
|
+
},
|
|
961
|
+
expectedOutput: "JSON array of release objects in the production stage",
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
description: "Get the next page of results",
|
|
965
|
+
parameters: {
|
|
966
|
+
nextUrl: "/projects/515fb9337c1074f6fd000003/releases?offset=30&per_page=30",
|
|
967
|
+
},
|
|
968
|
+
expectedOutput: "JSON array of release objects with metadata from the next page",
|
|
969
|
+
},
|
|
970
|
+
],
|
|
971
|
+
hints: ["For more detailed results use the Get Release tool"],
|
|
972
|
+
readOnly: true,
|
|
973
|
+
idempotent: true,
|
|
974
|
+
outputFormat: "JSON array of release summary objects with metadata",
|
|
975
|
+
}, async (args, _extra) => {
|
|
976
|
+
const { releases, nextUrl } = await this.listReleases((await this.getInputProject(args.projectId)).id, {
|
|
977
|
+
release_stage_name: args.releaseStage ?? "production",
|
|
978
|
+
visible_only: args.visibleOnly,
|
|
979
|
+
next_url: args.nextUrl ?? null,
|
|
980
|
+
});
|
|
981
|
+
return {
|
|
982
|
+
content: [
|
|
983
|
+
{
|
|
984
|
+
type: "text",
|
|
985
|
+
text: JSON.stringify({
|
|
986
|
+
releases,
|
|
987
|
+
next: nextUrl ?? null,
|
|
988
|
+
}),
|
|
989
|
+
},
|
|
990
|
+
],
|
|
991
|
+
};
|
|
992
|
+
});
|
|
993
|
+
register({
|
|
994
|
+
title: "Get Release",
|
|
995
|
+
summary: "Get more details for a specific release by its ID",
|
|
996
|
+
purpose: "Retrieve detailed information about a release for analysis and debugging",
|
|
997
|
+
useCases: [
|
|
998
|
+
"View release metadata such as version, source control info, and error counts",
|
|
999
|
+
"Analyze a specific release to correlate with error spikes or deployments",
|
|
1000
|
+
"See the stability targets for a project and if the release meets them",
|
|
1001
|
+
],
|
|
1002
|
+
parameters: [
|
|
1003
|
+
...(this.projectApiKey
|
|
1004
|
+
? []
|
|
1005
|
+
: [
|
|
1006
|
+
{
|
|
1007
|
+
name: "projectId",
|
|
1008
|
+
type: z.string(),
|
|
1009
|
+
description: "ID of the project containing the release",
|
|
1010
|
+
required: true,
|
|
1011
|
+
},
|
|
1012
|
+
]),
|
|
1013
|
+
{
|
|
1014
|
+
name: "releaseId",
|
|
1015
|
+
type: z.string(),
|
|
1016
|
+
description: "ID of the release to retrieve",
|
|
1017
|
+
required: true,
|
|
1018
|
+
examples: ["5f8d0d55c9e77c0017a1b2c3"],
|
|
1019
|
+
},
|
|
1020
|
+
],
|
|
1021
|
+
examples: [
|
|
1022
|
+
{
|
|
1023
|
+
description: "Get details for a specific release",
|
|
1024
|
+
parameters: {
|
|
1025
|
+
releaseId: "5f8d0d55c9e77c0017a1b2c3",
|
|
1026
|
+
},
|
|
1027
|
+
expectedOutput: "JSON object with release details including version, source control info, error counts and stability data.",
|
|
1028
|
+
},
|
|
1029
|
+
],
|
|
1030
|
+
hints: ["Release IDs can be found using the List releases tool"],
|
|
1031
|
+
readOnly: true,
|
|
1032
|
+
idempotent: true,
|
|
1033
|
+
outputFormat: "JSON object containing release details along with stability metrics such as user and session stability, and whether it meets project targets",
|
|
1034
|
+
}, async (args, _extra) => {
|
|
1035
|
+
if (!args.releaseId)
|
|
1036
|
+
throw new Error("releaseId argument is required");
|
|
1037
|
+
const release = await this.getRelease((await this.getInputProject(args.projectId)).id, args.releaseId);
|
|
1038
|
+
return {
|
|
1039
|
+
content: [{ type: "text", text: JSON.stringify(release) }],
|
|
1040
|
+
};
|
|
1041
|
+
});
|
|
1042
|
+
register({
|
|
1043
|
+
title: "List Builds in Release",
|
|
1044
|
+
summary: "List builds associated with a specific release",
|
|
1045
|
+
purpose: "Retrieve a list of builds for a given release to analyze deployment history and associated errors",
|
|
1046
|
+
useCases: [
|
|
1047
|
+
"View builds within a release to correlate with error spikes",
|
|
1048
|
+
"Analyze the composition of a release by examining its builds",
|
|
1049
|
+
],
|
|
1050
|
+
parameters: [
|
|
1051
|
+
...(this.projectApiKey
|
|
1052
|
+
? []
|
|
1053
|
+
: [
|
|
1054
|
+
{
|
|
1055
|
+
name: "projectId",
|
|
1056
|
+
type: z.string(),
|
|
1057
|
+
description: "ID of the project containing the release",
|
|
1058
|
+
required: true,
|
|
1059
|
+
},
|
|
1060
|
+
]),
|
|
1061
|
+
{
|
|
1062
|
+
name: "releaseId",
|
|
1063
|
+
type: z.string(),
|
|
1064
|
+
description: "ID of the release to list builds for",
|
|
1065
|
+
required: true,
|
|
1066
|
+
examples: ["5f8d0d55c9e77c0017a1b2c3"],
|
|
1067
|
+
},
|
|
1068
|
+
],
|
|
1069
|
+
examples: [
|
|
1070
|
+
{
|
|
1071
|
+
description: "List all builds in a specific release",
|
|
1072
|
+
parameters: {
|
|
1073
|
+
releaseId: "5f8d0d55c9e77c0017a1b2c3",
|
|
1074
|
+
},
|
|
1075
|
+
expectedOutput: "JSON array of build objects with metadata",
|
|
1076
|
+
},
|
|
1077
|
+
],
|
|
1078
|
+
hints: ["Release IDs can be found using the List releases tool"],
|
|
1079
|
+
readOnly: true,
|
|
1080
|
+
idempotent: true,
|
|
1081
|
+
outputFormat: "JSON array of build summary objects with metadata",
|
|
1082
|
+
}, async (args, _extra) => {
|
|
1083
|
+
if (!args.releaseId)
|
|
1084
|
+
throw new Error("releaseId argument is required");
|
|
1085
|
+
const builds = await this.listBuildsInRelease(args.releaseId);
|
|
1086
|
+
return {
|
|
1087
|
+
content: [{ type: "text", text: JSON.stringify(builds) }],
|
|
1088
|
+
};
|
|
1089
|
+
});
|
|
676
1090
|
}
|
|
677
1091
|
registerResources(register) {
|
|
678
1092
|
register("event", "{id}", async (uri, variables, _extra) => {
|
package/dist/common/server.js
CHANGED
|
@@ -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
|
}
|
|
@@ -48,6 +52,11 @@ export class SmartBearMcpServer extends McpServer {
|
|
|
48
52
|
});
|
|
49
53
|
});
|
|
50
54
|
}
|
|
55
|
+
if (client.registerPrompts) {
|
|
56
|
+
client.registerPrompts((name, config, cb) => {
|
|
57
|
+
return super.registerPrompt(name, config, cb);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
51
60
|
}
|
|
52
61
|
getAnnotations(toolTitle, params) {
|
|
53
62
|
const annotations = {
|
package/dist/index.js
CHANGED
|
@@ -38,11 +38,11 @@ async function main() {
|
|
|
38
38
|
}
|
|
39
39
|
if (pactBrokerUrl) {
|
|
40
40
|
if (pactBrokerToken) {
|
|
41
|
-
server.addClient(new PactflowClient(pactBrokerToken, pactBrokerUrl, "pactflow"));
|
|
41
|
+
server.addClient(new PactflowClient(pactBrokerToken, pactBrokerUrl, "pactflow", server.server));
|
|
42
42
|
client_defined = true;
|
|
43
43
|
}
|
|
44
44
|
else if (pactBrokerUsername && pactBrokerPassword) {
|
|
45
|
-
server.addClient(new PactflowClient({ username: pactBrokerUsername, password: pactBrokerPassword }, pactBrokerUrl, "pact_broker"));
|
|
45
|
+
server.addClient(new PactflowClient({ username: pactBrokerUsername, password: pactBrokerPassword }, pactBrokerUrl, "pact_broker", server.server));
|
|
46
46
|
client_defined = true;
|
|
47
47
|
}
|
|
48
48
|
else {
|
|
@@ -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.object({
|
|
150
|
+
total: z
|
|
151
|
+
.number()
|
|
152
|
+
.describe("The total number of AI credits available."),
|
|
153
|
+
used: z
|
|
154
|
+
.number()
|
|
155
|
+
.describe("The number of AI credits used."),
|
|
156
|
+
}).describe("AI credits information.");
|
|
157
|
+
export const OrganizationEntitlementsSchema = z.object({
|
|
158
|
+
name: z
|
|
159
|
+
.string()
|
|
160
|
+
.describe("The name of the organization."),
|
|
161
|
+
planAiEnabled: z
|
|
162
|
+
.boolean()
|
|
163
|
+
.describe("Whether AI features are enabled at the plan level."),
|
|
164
|
+
preferencesAiEnabled: z
|
|
165
|
+
.boolean()
|
|
166
|
+
.describe("Whether AI features are enabled at the preferences level."),
|
|
167
|
+
aiCredits: AiCreditsSchema.describe("AI credits information."),
|
|
168
|
+
}).describe("Organization entitlements information.");
|
|
169
|
+
export const UserEntitlementsSchema = z.object({
|
|
170
|
+
aiPermissions: z
|
|
171
|
+
.array(z.string())
|
|
172
|
+
.describe("List of AI permissions."),
|
|
173
|
+
}).describe("User entitlements information.");
|
|
174
|
+
export const EntitlementsSchema = z.object({
|
|
175
|
+
organizationEntitlements: OrganizationEntitlementsSchema.describe("Organization entitlements information."),
|
|
176
|
+
userEntitlements: UserEntitlementsSchema.describe("User entitlements information."),
|
|
177
|
+
}).describe("Entitlements information.");
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export const CanIDeploySchema = z.object({
|
|
3
|
-
pacticipant: z.string(),
|
|
4
|
-
version: z.string(),
|
|
5
|
-
environment: z.string(),
|
|
3
|
+
pacticipant: z.string().describe("The name of the pacticipant (application/service) being evaluated for deployment"),
|
|
4
|
+
version: z.string().describe("The version of the pacticipant that you want to check if it's safe to deploy"),
|
|
5
|
+
environment: z.string().describe("The target environment where the pacticipant version will be deployed (e.g., 'production', 'staging', 'test')"),
|
|
6
|
+
});
|
|
7
|
+
export const MatrixSchema = z.object({
|
|
8
|
+
latestby: z.string().optional().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."),
|
|
9
|
+
limit: z.number().min(1).max(1000).default(100).optional().describe("The limit on the number of results to return (1-1000, default: 100)"),
|
|
10
|
+
q: z.array(z.object({
|
|
11
|
+
pacticipant: z.string().describe("Name of the pacticipant (application)"),
|
|
12
|
+
version: z.string().optional().describe("Version number"),
|
|
13
|
+
branch: z.string().optional().describe("Name of the pacticipant version branch"),
|
|
14
|
+
environment: z.string().optional().describe("The name of the environment that the pacticipant version is deployed to"),
|
|
15
|
+
latest: z.boolean().optional().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."),
|
|
16
|
+
tag: z.string().optional().describe("The name of the pacticipant version tag (superseded by branch and environments)"),
|
|
17
|
+
mainBranch: z.boolean().optional().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."),
|
|
18
|
+
})).min(1).max(2)
|
|
6
19
|
});
|