@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/bugsnag/client.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
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
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { getNextUrlPathFromHeader } from "./client/api/base.js";
|
|
5
|
+
import { FilterObjectSchema, toQueryString, } from "./client/api/filters.js";
|
|
6
|
+
import { ProjectAPI, } from "./client/api/Project.js";
|
|
7
|
+
import { Configuration, CurrentUserAPI, ErrorAPI } from "./client/index.js";
|
|
7
8
|
const HUB_PREFIX = "00000";
|
|
8
9
|
const DEFAULT_DOMAIN = "bugsnag.com";
|
|
9
10
|
const HUB_DOMAIN = "bugsnag.smartbear.com";
|
|
@@ -12,10 +13,13 @@ 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([
|
|
18
|
-
"search" // This is searches multiple fields and is more a convenience for humans, we're removing to avoid over-matching
|
|
22
|
+
"search", // This is searches multiple fields and is more a convenience for humans, we're removing to avoid over-matching
|
|
19
23
|
]);
|
|
20
24
|
const PERMITTED_UPDATE_OPERATIONS = [
|
|
21
25
|
"override_severity",
|
|
@@ -23,7 +27,7 @@ const PERMITTED_UPDATE_OPERATIONS = [
|
|
|
23
27
|
"fix",
|
|
24
28
|
"ignore",
|
|
25
29
|
"discard",
|
|
26
|
-
"undiscard"
|
|
30
|
+
"undiscard",
|
|
27
31
|
];
|
|
28
32
|
export class BugsnagClient {
|
|
29
33
|
currentUserApi;
|
|
@@ -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,17 +54,35 @@ 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
|
-
if (apiKey
|
|
85
|
+
if (apiKey?.startsWith(HUB_PREFIX)) {
|
|
64
86
|
return `https://${subdomain}.${HUB_DOMAIN}`;
|
|
65
87
|
}
|
|
66
88
|
else {
|
|
@@ -73,7 +95,7 @@ export class BugsnagClient {
|
|
|
73
95
|
getEndpoint(subdomain, apiKey, endpoint) {
|
|
74
96
|
let subDomainEndpoint;
|
|
75
97
|
if (!endpoint) {
|
|
76
|
-
if (apiKey
|
|
98
|
+
if (apiKey?.startsWith(HUB_PREFIX)) {
|
|
77
99
|
subDomainEndpoint = `https://${subdomain}.${HUB_DOMAIN}`;
|
|
78
100
|
}
|
|
79
101
|
else {
|
|
@@ -83,7 +105,8 @@ export class BugsnagClient {
|
|
|
83
105
|
else {
|
|
84
106
|
// check if the endpoint matches either the HUB_DOMAIN or DEFAULT_DOMAIN
|
|
85
107
|
const url = new URL(endpoint);
|
|
86
|
-
if (url.hostname.endsWith(HUB_DOMAIN) ||
|
|
108
|
+
if (url.hostname.endsWith(HUB_DOMAIN) ||
|
|
109
|
+
url.hostname.endsWith(DEFAULT_DOMAIN)) {
|
|
87
110
|
// For known domains (Hub or Bugsnag), always use HTTPS and standard format
|
|
88
111
|
if (url.hostname.endsWith(HUB_DOMAIN)) {
|
|
89
112
|
subDomainEndpoint = `https://${subdomain}.${HUB_DOMAIN}`;
|
|
@@ -102,7 +125,7 @@ export class BugsnagClient {
|
|
|
102
125
|
async getDashboardUrl(project) {
|
|
103
126
|
return `${this.appEndpoint}/${(await this.getOrganization()).slug}/${project.slug}`;
|
|
104
127
|
}
|
|
105
|
-
async getErrorUrl(project, errorId, queryString =
|
|
128
|
+
async getErrorUrl(project, errorId, queryString = "") {
|
|
106
129
|
const dashboardUrl = await this.getDashboardUrl(project);
|
|
107
130
|
return `${dashboardUrl}/errors/${errorId}${queryString}`;
|
|
108
131
|
}
|
|
@@ -127,10 +150,7 @@ export class BugsnagClient {
|
|
|
127
150
|
let projects = this.cache.get(cacheKeys.PROJECTS);
|
|
128
151
|
if (!projects) {
|
|
129
152
|
const org = await this.getOrganization();
|
|
130
|
-
const
|
|
131
|
-
paginate: true
|
|
132
|
-
};
|
|
133
|
-
const response = await this.currentUserApi.getOrganizationProjects(org.id, options);
|
|
153
|
+
const response = await this.currentUserApi.getOrganizationProjects(org.id);
|
|
134
154
|
projects = response.body || [];
|
|
135
155
|
this.cache.set(cacheKeys.PROJECTS, projects);
|
|
136
156
|
}
|
|
@@ -146,7 +166,7 @@ export class BugsnagClient {
|
|
|
146
166
|
const projects = await this.getProjects();
|
|
147
167
|
project = projects.find((p) => p.api_key === this.projectApiKey) ?? null;
|
|
148
168
|
if (!project) {
|
|
149
|
-
throw new Error(
|
|
169
|
+
throw new Error("Unable to find project with the configured API key.");
|
|
150
170
|
}
|
|
151
171
|
this.cache.set(cacheKeys.CURRENT_PROJECT, project);
|
|
152
172
|
if (project) {
|
|
@@ -160,24 +180,26 @@ export class BugsnagClient {
|
|
|
160
180
|
if (!filtersResponse || filtersResponse.length === 0) {
|
|
161
181
|
throw new Error(`No event fields found for project ${project.name}.`);
|
|
162
182
|
}
|
|
163
|
-
filtersResponse = filtersResponse.filter(field => !EXCLUDED_EVENT_FIELDS.has(field.display_id));
|
|
183
|
+
filtersResponse = filtersResponse.filter((field) => !EXCLUDED_EVENT_FIELDS.has(field.display_id));
|
|
164
184
|
return filtersResponse;
|
|
165
185
|
}
|
|
166
186
|
async getEvent(eventId, projectId) {
|
|
167
|
-
const projectIds = projectId
|
|
168
|
-
|
|
169
|
-
|
|
187
|
+
const projectIds = projectId
|
|
188
|
+
? [projectId]
|
|
189
|
+
: (await this.getProjects()).map((p) => p.id);
|
|
190
|
+
const projectEvents = await Promise.all(projectIds.map((projectId) => this.errorsApi.viewEventById(projectId, eventId).catch((_e) => null)));
|
|
191
|
+
return projectEvents.find((event) => event && !!event.body)?.body || null;
|
|
170
192
|
}
|
|
171
193
|
async updateError(projectId, errorId, operation, options) {
|
|
172
194
|
const errorUpdateRequest = {
|
|
173
195
|
operation: operation,
|
|
174
|
-
...options
|
|
196
|
+
...options,
|
|
175
197
|
};
|
|
176
198
|
const response = await this.errorsApi.updateErrorOnProject(projectId, errorId, errorUpdateRequest);
|
|
177
199
|
return response.status === 200 || response.status === 204;
|
|
178
200
|
}
|
|
179
201
|
async getInputProject(projectId) {
|
|
180
|
-
if (typeof projectId ===
|
|
202
|
+
if (typeof projectId === "string") {
|
|
181
203
|
const maybeProject = await this.getProject(projectId);
|
|
182
204
|
if (!maybeProject) {
|
|
183
205
|
throw new Error(`Project with ID ${projectId} not found.`);
|
|
@@ -187,11 +209,91 @@ export class BugsnagClient {
|
|
|
187
209
|
else {
|
|
188
210
|
const currentProject = await this.getCurrentProject();
|
|
189
211
|
if (!currentProject) {
|
|
190
|
-
throw new Error(
|
|
212
|
+
throw new Error("No current project found. Please provide a projectId or configure a project API key.");
|
|
191
213
|
}
|
|
192
214
|
return currentProject;
|
|
193
215
|
}
|
|
194
216
|
}
|
|
217
|
+
async listBuilds(projectId, opts) {
|
|
218
|
+
const response = await this.projectApi.listBuilds(projectId, opts);
|
|
219
|
+
const fetchedBuilds = response.body || [];
|
|
220
|
+
const nextUrl = getNextUrlPathFromHeader(response.headers, this.apiEndpoint);
|
|
221
|
+
const stabilityTargets = await this.getProjectStabilityTargets(projectId);
|
|
222
|
+
const formattedBuilds = fetchedBuilds.map((b) => this.addStabilityData(b, stabilityTargets));
|
|
223
|
+
return { builds: formattedBuilds, nextUrl };
|
|
224
|
+
}
|
|
225
|
+
async getBuild(projectId, buildId) {
|
|
226
|
+
const cacheKey = `${cacheKeys.BUILD}_${buildId}`;
|
|
227
|
+
const build = this.cache.get(cacheKey);
|
|
228
|
+
if (build)
|
|
229
|
+
return build;
|
|
230
|
+
const fetchedBuild = (await this.projectApi.getBuild(projectId, buildId))
|
|
231
|
+
.body;
|
|
232
|
+
if (!fetchedBuild)
|
|
233
|
+
throw new Error(`No build for ${buildId} found.`);
|
|
234
|
+
const stabilityTargets = await this.getProjectStabilityTargets(projectId);
|
|
235
|
+
const formattedBuild = this.addStabilityData(fetchedBuild, stabilityTargets);
|
|
236
|
+
this.cache.set(cacheKey, formattedBuild, 5 * 60);
|
|
237
|
+
return formattedBuild;
|
|
238
|
+
}
|
|
239
|
+
async listReleases(projectId, opts) {
|
|
240
|
+
const response = await this.projectApi.listReleases(projectId, opts);
|
|
241
|
+
const fetchedReleases = response.body || [];
|
|
242
|
+
const nextUrl = getNextUrlPathFromHeader(response.headers, this.apiEndpoint);
|
|
243
|
+
const stabilityTargets = await this.getProjectStabilityTargets(projectId);
|
|
244
|
+
const formattedReleases = fetchedReleases.map((r) => this.addStabilityData(r, stabilityTargets));
|
|
245
|
+
return { releases: formattedReleases, nextUrl };
|
|
246
|
+
}
|
|
247
|
+
async getRelease(projectId, releaseId) {
|
|
248
|
+
const cacheKey = `${cacheKeys.RELEASE}_${releaseId}`;
|
|
249
|
+
const release = this.cache.get(cacheKey);
|
|
250
|
+
if (release)
|
|
251
|
+
return release;
|
|
252
|
+
const fetchedRelease = (await this.projectApi.getRelease(releaseId)).body;
|
|
253
|
+
if (!fetchedRelease)
|
|
254
|
+
throw new Error(`No release for ${releaseId} found.`);
|
|
255
|
+
const stabilityTargets = await this.getProjectStabilityTargets(projectId);
|
|
256
|
+
const formattedRelease = this.addStabilityData(fetchedRelease, stabilityTargets);
|
|
257
|
+
this.cache.set(cacheKey, formattedRelease, 5 * 60);
|
|
258
|
+
return formattedRelease;
|
|
259
|
+
}
|
|
260
|
+
async listBuildsInRelease(releaseId) {
|
|
261
|
+
const cacheKey = `${cacheKeys.BUILDS_IN_RELEASE}_${releaseId}`;
|
|
262
|
+
const builds = this.cache.get(cacheKey);
|
|
263
|
+
if (builds)
|
|
264
|
+
return builds;
|
|
265
|
+
const fetchedBuilds = (await this.projectApi.listBuildsInRelease(releaseId)).body || [];
|
|
266
|
+
this.cache.set(cacheKey, fetchedBuilds, 5 * 60);
|
|
267
|
+
return fetchedBuilds;
|
|
268
|
+
}
|
|
269
|
+
async getProjectStabilityTargets(projectId) {
|
|
270
|
+
return await this.projectApi.getProjectStabilityTargets(projectId);
|
|
271
|
+
}
|
|
272
|
+
addStabilityData(source, stabilityTargets) {
|
|
273
|
+
const { stability_target_type, target_stability, critical_stability } = stabilityTargets;
|
|
274
|
+
const user_stability = source.accumulative_daily_users_seen === 0 // avoid division by zero
|
|
275
|
+
? 0
|
|
276
|
+
: (source.accumulative_daily_users_seen -
|
|
277
|
+
source.accumulative_daily_users_with_unhandled) /
|
|
278
|
+
source.accumulative_daily_users_seen;
|
|
279
|
+
const session_stability = source.total_sessions_count === 0 // avoid division by zero
|
|
280
|
+
? 0
|
|
281
|
+
: (source.total_sessions_count - source.unhandled_sessions_count) /
|
|
282
|
+
source.total_sessions_count;
|
|
283
|
+
const stabilityMetric = stability_target_type === "user" ? user_stability : session_stability;
|
|
284
|
+
const meets_target_stability = stabilityMetric >= target_stability.value;
|
|
285
|
+
const meets_critical_stability = stabilityMetric >= critical_stability.value;
|
|
286
|
+
return {
|
|
287
|
+
...source,
|
|
288
|
+
user_stability,
|
|
289
|
+
session_stability,
|
|
290
|
+
stability_target_type,
|
|
291
|
+
target_stability: target_stability.value,
|
|
292
|
+
critical_stability: critical_stability.value,
|
|
293
|
+
meets_target_stability,
|
|
294
|
+
meets_critical_stability,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
195
297
|
registerTools(register, getInput) {
|
|
196
298
|
if (!this.projectApiKey) {
|
|
197
299
|
register({
|
|
@@ -201,7 +303,7 @@ export class BugsnagClient {
|
|
|
201
303
|
useCases: [
|
|
202
304
|
"Browse available projects when no specific project API key is configured",
|
|
203
305
|
"Find project IDs needed for other tools",
|
|
204
|
-
"Get an overview of all projects in the organization"
|
|
306
|
+
"Get an overview of all projects in the organization",
|
|
205
307
|
],
|
|
206
308
|
parameters: [
|
|
207
309
|
{
|
|
@@ -209,35 +311,35 @@ export class BugsnagClient {
|
|
|
209
311
|
type: z.number(),
|
|
210
312
|
description: "Number of projects to return per page for pagination",
|
|
211
313
|
required: false,
|
|
212
|
-
examples: ["10", "25", "50"]
|
|
314
|
+
examples: ["10", "25", "50"],
|
|
213
315
|
},
|
|
214
316
|
{
|
|
215
317
|
name: "page",
|
|
216
318
|
type: z.number(),
|
|
217
319
|
description: "Page number to return (starts from 1)",
|
|
218
320
|
required: false,
|
|
219
|
-
examples: ["1", "2", "3"]
|
|
220
|
-
}
|
|
321
|
+
examples: ["1", "2", "3"],
|
|
322
|
+
},
|
|
221
323
|
],
|
|
222
324
|
examples: [
|
|
223
325
|
{
|
|
224
326
|
description: "Get first 10 projects",
|
|
225
327
|
parameters: {
|
|
226
328
|
page_size: 10,
|
|
227
|
-
page: 1
|
|
329
|
+
page: 1,
|
|
228
330
|
},
|
|
229
331
|
expectedOutput: "JSON array of project objects with IDs, names, and metadata",
|
|
230
332
|
},
|
|
231
333
|
{
|
|
232
334
|
description: "Get all projects (no pagination)",
|
|
233
335
|
parameters: {},
|
|
234
|
-
expectedOutput: "JSON array of all available projects"
|
|
235
|
-
}
|
|
336
|
+
expectedOutput: "JSON array of all available projects",
|
|
337
|
+
},
|
|
236
338
|
],
|
|
237
339
|
hints: [
|
|
238
340
|
"Use pagination for organizations with many projects to avoid large responses",
|
|
239
|
-
"Project IDs from this list can be used with other tools when no project API key is configured"
|
|
240
|
-
]
|
|
341
|
+
"Project IDs from this list can be used with other tools when no project API key is configured",
|
|
342
|
+
],
|
|
241
343
|
}, async (args, _extra) => {
|
|
242
344
|
let projects = await this.getProjects();
|
|
243
345
|
if (!projects || projects.length === 0) {
|
|
@@ -267,7 +369,7 @@ export class BugsnagClient {
|
|
|
267
369
|
"Investigate a specific error found through the List Project Errors tool",
|
|
268
370
|
"Understand which types of user are affected by the error using summarized event data",
|
|
269
371
|
"Get error details for debugging and root cause analysis",
|
|
270
|
-
"Retrieve error metadata for incident reports and documentation"
|
|
372
|
+
"Retrieve error metadata for incident reports and documentation",
|
|
271
373
|
],
|
|
272
374
|
parameters: [
|
|
273
375
|
{
|
|
@@ -275,16 +377,18 @@ export class BugsnagClient {
|
|
|
275
377
|
type: z.string(),
|
|
276
378
|
required: true,
|
|
277
379
|
description: "Unique identifier of the error to retrieve",
|
|
278
|
-
examples: ["6863e2af8c857c0a5023b411"]
|
|
380
|
+
examples: ["6863e2af8c857c0a5023b411"],
|
|
279
381
|
},
|
|
280
|
-
...(this.projectApiKey
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
382
|
+
...(this.projectApiKey
|
|
383
|
+
? []
|
|
384
|
+
: [
|
|
385
|
+
{
|
|
386
|
+
name: "projectId",
|
|
387
|
+
type: z.string(),
|
|
388
|
+
required: true,
|
|
389
|
+
description: "ID of the project containing the error",
|
|
390
|
+
},
|
|
391
|
+
]),
|
|
288
392
|
{
|
|
289
393
|
name: "filters",
|
|
290
394
|
type: FilterObjectSchema,
|
|
@@ -294,14 +398,14 @@ export class BugsnagClient {
|
|
|
294
398
|
'{"error.status": [{"type": "eq", "value": "open"}]}',
|
|
295
399
|
'{"event.since": [{"type": "eq", "value": "7d"}]} // Relative time: last 7 days',
|
|
296
400
|
'{"event.since": [{"type": "eq", "value": "2018-05-20T00:00:00Z"}]} // ISO 8601 UTC format',
|
|
297
|
-
'{"user.email": [{"type": "eq", "value": "user@example.com"}]}'
|
|
401
|
+
'{"user.email": [{"type": "eq", "value": "user@example.com"}]}',
|
|
298
402
|
],
|
|
299
403
|
constraints: [
|
|
300
404
|
"Time filters support ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h)",
|
|
301
405
|
"ISO 8601 times must be in UTC and use extended format",
|
|
302
|
-
"Relative time periods: h (hours), d (days)"
|
|
303
|
-
]
|
|
304
|
-
}
|
|
406
|
+
"Relative time periods: h (hours), d (days)",
|
|
407
|
+
],
|
|
408
|
+
},
|
|
305
409
|
],
|
|
306
410
|
outputFormat: "JSON object containing: " +
|
|
307
411
|
" - error_details: Aggregated data about the error, including first and last seen occurrence" +
|
|
@@ -312,10 +416,10 @@ export class BugsnagClient {
|
|
|
312
416
|
{
|
|
313
417
|
description: "Get details for a specific error",
|
|
314
418
|
parameters: {
|
|
315
|
-
errorId: "6863e2af8c857c0a5023b411"
|
|
419
|
+
errorId: "6863e2af8c857c0a5023b411",
|
|
316
420
|
},
|
|
317
|
-
expectedOutput: "JSON object with error details including message, stack trace, occurrence count, and metadata"
|
|
318
|
-
}
|
|
421
|
+
expectedOutput: "JSON object with error details including message, stack trace, occurrence count, and metadata",
|
|
422
|
+
},
|
|
319
423
|
],
|
|
320
424
|
hints: [
|
|
321
425
|
"Error IDs can be found using the List Project Errors tool",
|
|
@@ -334,13 +438,13 @@ export class BugsnagClient {
|
|
|
334
438
|
// Build query parameters
|
|
335
439
|
const params = new URLSearchParams();
|
|
336
440
|
// Add sorting and pagination parameters to get the latest event
|
|
337
|
-
params.append(
|
|
338
|
-
params.append(
|
|
339
|
-
params.append(
|
|
340
|
-
params.append(
|
|
441
|
+
params.append("sort", "timestamp");
|
|
442
|
+
params.append("direction", "desc");
|
|
443
|
+
params.append("per_page", "1");
|
|
444
|
+
params.append("full_reports", "true");
|
|
341
445
|
const filters = {
|
|
342
|
-
|
|
343
|
-
...args.filters
|
|
446
|
+
error: [{ type: "eq", value: args.errorId }],
|
|
447
|
+
...args.filters,
|
|
344
448
|
};
|
|
345
449
|
const filtersQueryString = toQueryString(filters);
|
|
346
450
|
const listEventsQueryString = `?${params}&${filtersQueryString}`;
|
|
@@ -358,11 +462,12 @@ export class BugsnagClient {
|
|
|
358
462
|
const content = {
|
|
359
463
|
error_details: errorDetails,
|
|
360
464
|
latest_event: latestEvent,
|
|
361
|
-
pivots: (await this.errorsApi.listErrorPivots(project.id, args.errorId))
|
|
465
|
+
pivots: (await this.errorsApi.listErrorPivots(project.id, args.errorId))
|
|
466
|
+
.body || [],
|
|
362
467
|
url: await this.getErrorUrl(project, args.errorId, `?${filtersQueryString}`),
|
|
363
468
|
};
|
|
364
469
|
return {
|
|
365
|
-
content: [{ type: "text", text: JSON.stringify(content) }]
|
|
470
|
+
content: [{ type: "text", text: JSON.stringify(content) }],
|
|
366
471
|
};
|
|
367
472
|
});
|
|
368
473
|
register({
|
|
@@ -372,7 +477,7 @@ export class BugsnagClient {
|
|
|
372
477
|
useCases: [
|
|
373
478
|
"Get event details when given a dashboard URL from a user or notification",
|
|
374
479
|
"Extract event information from shared links or browser URLs",
|
|
375
|
-
"Quick lookup of event details without needing separate project and event IDs"
|
|
480
|
+
"Quick lookup of event details without needing separate project and event IDs",
|
|
376
481
|
],
|
|
377
482
|
parameters: [
|
|
378
483
|
{
|
|
@@ -381,32 +486,32 @@ export class BugsnagClient {
|
|
|
381
486
|
description: "Full URL to the event details page in the BugSnag dashboard (web interface)",
|
|
382
487
|
required: true,
|
|
383
488
|
examples: [
|
|
384
|
-
"https://app.bugsnag.com/my-org/my-project/errors/6863e2af8c857c0a5023b411?event_id=6863e2af012caf1d5c320000"
|
|
489
|
+
"https://app.bugsnag.com/my-org/my-project/errors/6863e2af8c857c0a5023b411?event_id=6863e2af012caf1d5c320000",
|
|
385
490
|
],
|
|
386
491
|
constraints: [
|
|
387
|
-
"Must be a valid dashboard URL containing project slug and event_id parameter"
|
|
388
|
-
]
|
|
389
|
-
}
|
|
492
|
+
"Must be a valid dashboard URL containing project slug and event_id parameter",
|
|
493
|
+
],
|
|
494
|
+
},
|
|
390
495
|
],
|
|
391
496
|
examples: [
|
|
392
497
|
{
|
|
393
498
|
description: "Get event details from a dashboard URL",
|
|
394
499
|
parameters: {
|
|
395
|
-
link: "https://app.bugsnag.com/my-org/my-project/errors/6863e2af8c857c0a5023b411?event_id=6863e2af012caf1d5c320000"
|
|
500
|
+
link: "https://app.bugsnag.com/my-org/my-project/errors/6863e2af8c857c0a5023b411?event_id=6863e2af012caf1d5c320000",
|
|
396
501
|
},
|
|
397
|
-
expectedOutput: "JSON object with complete event details including stack trace, metadata, and context"
|
|
398
|
-
}
|
|
502
|
+
expectedOutput: "JSON object with complete event details including stack trace, metadata, and context",
|
|
503
|
+
},
|
|
399
504
|
],
|
|
400
505
|
hints: [
|
|
401
506
|
"The URL must contain both project slug in the path and event_id in query parameters",
|
|
402
|
-
"This is useful when users share BugSnag dashboard URLs and you need to extract the event data"
|
|
403
|
-
]
|
|
507
|
+
"This is useful when users share BugSnag dashboard URLs and you need to extract the event data",
|
|
508
|
+
],
|
|
404
509
|
}, async (args, _extra) => {
|
|
405
510
|
if (!args.link)
|
|
406
511
|
throw new Error("link argument is required");
|
|
407
512
|
const url = new URL(args.link);
|
|
408
513
|
const eventId = url.searchParams.get("event_id");
|
|
409
|
-
const projectSlug = url.pathname.split(
|
|
514
|
+
const projectSlug = url.pathname.split("/")[2];
|
|
410
515
|
if (!projectSlug || !eventId)
|
|
411
516
|
throw new Error("Both projectSlug and eventId must be present in the link");
|
|
412
517
|
// get the project id from list of projects
|
|
@@ -420,7 +525,6 @@ export class BugsnagClient {
|
|
|
420
525
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
421
526
|
};
|
|
422
527
|
});
|
|
423
|
-
// Dynamically infer the filters schema from cached project event fields
|
|
424
528
|
register({
|
|
425
529
|
title: "List Project Errors",
|
|
426
530
|
summary: "List and search errors in a project using customizable filters and pagination",
|
|
@@ -429,122 +533,138 @@ export class BugsnagClient {
|
|
|
429
533
|
"Debug recent application errors by filtering for open errors in the last 7 days",
|
|
430
534
|
"Generate error reports for stakeholders by filtering specific error types or severity levels",
|
|
431
535
|
"Monitor error trends over time using date range filters",
|
|
432
|
-
"Find errors affecting specific users or environments using metadata filters"
|
|
536
|
+
"Find errors affecting specific users or environments using metadata filters",
|
|
433
537
|
],
|
|
434
538
|
parameters: [
|
|
435
539
|
{
|
|
436
540
|
name: "filters",
|
|
437
|
-
type: FilterObjectSchema
|
|
541
|
+
type: FilterObjectSchema.default({
|
|
542
|
+
"event.since": [{ type: "eq", value: "30d" }],
|
|
543
|
+
"error.status": [{ type: "eq", value: "open" }],
|
|
544
|
+
}),
|
|
438
545
|
description: "Apply filters to narrow down the error list. Use the List Project Event Filters tool to discover available filter fields",
|
|
439
546
|
required: false,
|
|
440
547
|
examples: [
|
|
441
548
|
'{"error.status": [{"type": "eq", "value": "open"}]}',
|
|
442
549
|
'{"event.since": [{"type": "eq", "value": "7d"}]} // Relative time: last 7 days',
|
|
443
550
|
'{"event.since": [{"type": "eq", "value": "2018-05-20T00:00:00Z"}]} // ISO 8601 UTC format',
|
|
444
|
-
'{"user.email": [{"type": "eq", "value": "user@example.com"}]}'
|
|
551
|
+
'{"user.email": [{"type": "eq", "value": "user@example.com"}]}',
|
|
445
552
|
],
|
|
446
553
|
constraints: [
|
|
447
554
|
"Time filters support ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h)",
|
|
448
555
|
"ISO 8601 times must be in UTC and use extended format",
|
|
449
|
-
"Relative time periods: h (hours), d (days)"
|
|
450
|
-
]
|
|
556
|
+
"Relative time periods: h (hours), d (days)",
|
|
557
|
+
],
|
|
451
558
|
},
|
|
452
559
|
{
|
|
453
560
|
name: "sort",
|
|
454
|
-
type: z
|
|
455
|
-
|
|
561
|
+
type: z
|
|
562
|
+
.enum(["first_seen", "last_seen", "events", "users", "unsorted"])
|
|
563
|
+
.default("last_seen"),
|
|
564
|
+
description: "Field to sort the errors by",
|
|
456
565
|
required: false,
|
|
457
|
-
examples: ["last_seen"]
|
|
566
|
+
examples: ["last_seen"],
|
|
458
567
|
},
|
|
459
568
|
{
|
|
460
569
|
name: "direction",
|
|
461
570
|
type: z.enum(["asc", "desc"]).default("desc"),
|
|
462
571
|
description: "Sort direction for ordering results",
|
|
463
572
|
required: false,
|
|
464
|
-
examples: ["desc"]
|
|
573
|
+
examples: ["desc"],
|
|
465
574
|
},
|
|
466
575
|
{
|
|
467
576
|
name: "per_page",
|
|
468
|
-
type: z.number().min(1).max(100),
|
|
577
|
+
type: z.number().min(1).max(100).default(30),
|
|
469
578
|
description: "How many results to return per page.",
|
|
470
579
|
required: false,
|
|
471
|
-
examples: ["30", "50", "100"]
|
|
580
|
+
examples: ["30", "50", "100"],
|
|
472
581
|
},
|
|
473
582
|
{
|
|
474
583
|
name: "next",
|
|
475
584
|
type: z.string().url(),
|
|
476
585
|
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.",
|
|
477
586
|
required: false,
|
|
478
|
-
examples: [
|
|
479
|
-
|
|
587
|
+
examples: [
|
|
588
|
+
"https://api.bugsnag.com/projects/515fb9337c1074f6fd000003/errors?offset=30&per_page=30&sort=last_seen",
|
|
589
|
+
],
|
|
590
|
+
constraints: [
|
|
591
|
+
"Only values provided in the output from this tool can be used. Do not attempt to construct it manually.",
|
|
592
|
+
],
|
|
480
593
|
},
|
|
481
|
-
...(this.projectApiKey
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
594
|
+
...(this.projectApiKey
|
|
595
|
+
? []
|
|
596
|
+
: [
|
|
597
|
+
{
|
|
598
|
+
name: "projectId",
|
|
599
|
+
type: z.string(),
|
|
600
|
+
description: "ID of the project to query for errors",
|
|
601
|
+
required: true,
|
|
602
|
+
},
|
|
603
|
+
]),
|
|
489
604
|
],
|
|
490
605
|
examples: [
|
|
491
606
|
{
|
|
492
607
|
description: "Find errors affecting a specific user in the last 24 hours",
|
|
493
608
|
parameters: {
|
|
494
609
|
filters: {
|
|
495
|
-
"user.email": [{
|
|
496
|
-
"event.since": [{
|
|
497
|
-
}
|
|
610
|
+
"user.email": [{ type: "eq", value: "user@example.com" }],
|
|
611
|
+
"event.since": [{ type: "eq", value: "24h" }],
|
|
612
|
+
},
|
|
498
613
|
},
|
|
499
|
-
expectedOutput: "JSON object with a list of errors in the 'data' field, a count of the current page of results in the 'count' field, and a total count of all results in the 'total' field"
|
|
614
|
+
expectedOutput: "JSON object with a list of errors in the 'data' field, a count of the current page of results in the 'count' field, and a total count of all results in the 'total' field",
|
|
500
615
|
},
|
|
501
616
|
{
|
|
502
617
|
description: "Get the 10 open errors with the most users affected in the last 30 days",
|
|
503
618
|
parameters: {
|
|
504
619
|
filters: {
|
|
505
|
-
"event.since": [{
|
|
506
|
-
"error.status": [{
|
|
620
|
+
"event.since": [{ type: "eq", value: "30d" }],
|
|
621
|
+
"error.status": [{ type: "eq", value: "open" }],
|
|
507
622
|
},
|
|
508
623
|
sort: "users",
|
|
509
624
|
direction: "desc",
|
|
510
|
-
per_page: 10
|
|
625
|
+
per_page: 10,
|
|
511
626
|
},
|
|
512
|
-
expectedOutput: "JSON object with a list of errors in the 'data' field, a count of the current page of results in the 'count' field, and a total count of all results in the 'total' field"
|
|
627
|
+
expectedOutput: "JSON object with a list of errors in the 'data' field, a count of the current page of results in the 'count' field, and a total count of all results in the 'total' field",
|
|
513
628
|
},
|
|
514
629
|
{
|
|
515
630
|
description: "Get the next 50 results",
|
|
516
631
|
parameters: {
|
|
517
632
|
next: "https://api.bugsnag.com/projects/515fb9337c1074f6fd000003/errors?base=2025-08-29T13%3A11%3A37Z&direction=desc&filters%5Berror.status%5D%5B%5D%5Btype%5D=eq&filters%5Berror.status%5D%5B%5D%5Bvalue%5D=open&offset=10&per_page=10&sort=users",
|
|
518
|
-
per_page: 50
|
|
633
|
+
per_page: 50,
|
|
519
634
|
},
|
|
520
|
-
expectedOutput: "JSON object with a list of errors in the 'data' field, a count of the current page of results in the 'count' field, and a total count of all results in the 'total' field"
|
|
521
|
-
}
|
|
635
|
+
expectedOutput: "JSON object with a list of errors in the 'data' field, a count of the current page of results in the 'count' field, and a total count of all results in the 'total' field",
|
|
636
|
+
},
|
|
522
637
|
],
|
|
523
638
|
hints: [
|
|
524
639
|
"Use list_project_event_filters tool first to discover valid filter field names for your project",
|
|
525
640
|
"Combine multiple filters to narrow results - filters are applied with AND logic",
|
|
526
641
|
"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
642
|
"Common time filters: event.since (from this time), event.before (until this time)",
|
|
643
|
+
"The 'event.since' filter and 'error.status' filters are always applied and if not specified are set to '30d' and 'open' respectively",
|
|
528
644
|
"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
645
|
"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
646
|
"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.",
|
|
531
|
-
"Do not modify the next URL as this can cause incorrect results. The only other parameter that can be used with 'next' is 'per_page' to control the page size."
|
|
532
|
-
]
|
|
647
|
+
"Do not modify the next URL as this can cause incorrect results. The only other parameter that can be used with 'next' is 'per_page' to control the page size.",
|
|
648
|
+
],
|
|
533
649
|
}, async (args, _extra) => {
|
|
534
650
|
const project = await this.getInputProject(args.projectId);
|
|
535
651
|
// Validate filter keys against cached event fields
|
|
536
652
|
if (args.filters) {
|
|
537
653
|
const eventFields = this.cache.get(cacheKeys.CURRENT_PROJECT_EVENT_FILTERS) || [];
|
|
538
|
-
const validKeys = new Set(eventFields.map(f => f.display_id));
|
|
654
|
+
const validKeys = new Set(eventFields.map((f) => f.display_id));
|
|
539
655
|
for (const key of Object.keys(args.filters)) {
|
|
540
656
|
if (!validKeys.has(key)) {
|
|
541
657
|
throw new Error(`Invalid filter key: ${key}`);
|
|
542
658
|
}
|
|
543
659
|
}
|
|
544
660
|
}
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
661
|
+
const defaultFilters = {
|
|
662
|
+
"event.since": [{ type: "eq", value: "30d" }],
|
|
663
|
+
"error.status": [{ type: "eq", value: "open" }],
|
|
664
|
+
};
|
|
665
|
+
const options = {
|
|
666
|
+
filters: { ...defaultFilters, ...args.filters },
|
|
667
|
+
};
|
|
548
668
|
if (args.sort !== undefined)
|
|
549
669
|
options.sort = args.sort;
|
|
550
670
|
if (args.direction !== undefined)
|
|
@@ -555,12 +675,12 @@ export class BugsnagClient {
|
|
|
555
675
|
options.next = args.next;
|
|
556
676
|
const response = await this.errorsApi.listProjectErrors(project.id, options);
|
|
557
677
|
const errors = response.body || [];
|
|
558
|
-
const totalCount = response.headers.get(
|
|
559
|
-
const linkHeader = response.headers.get(
|
|
678
|
+
const totalCount = response.headers.get("X-Total-Count");
|
|
679
|
+
const linkHeader = response.headers.get("Link");
|
|
560
680
|
const result = {
|
|
561
681
|
data: errors,
|
|
562
682
|
count: errors.length,
|
|
563
|
-
total: totalCount ? parseInt(totalCount) : undefined,
|
|
683
|
+
total: totalCount ? parseInt(totalCount, 10) : undefined,
|
|
564
684
|
next: linkHeader?.match(/<([^>]+)>/)?.[1],
|
|
565
685
|
};
|
|
566
686
|
return {
|
|
@@ -574,20 +694,20 @@ export class BugsnagClient {
|
|
|
574
694
|
useCases: [
|
|
575
695
|
"Discover what filter fields are available before searching for errors",
|
|
576
696
|
"Find the correct field names for filtering by user, environment, or custom metadata",
|
|
577
|
-
"Understand filter options and data types for building complex queries"
|
|
697
|
+
"Understand filter options and data types for building complex queries",
|
|
578
698
|
],
|
|
579
699
|
parameters: [],
|
|
580
700
|
examples: [
|
|
581
701
|
{
|
|
582
702
|
description: "Get all available filter fields",
|
|
583
703
|
parameters: {},
|
|
584
|
-
expectedOutput: "JSON array of EventField objects containing display_id, custom flag, and filter/pivot options"
|
|
585
|
-
}
|
|
704
|
+
expectedOutput: "JSON array of EventField objects containing display_id, custom flag, and filter/pivot options",
|
|
705
|
+
},
|
|
586
706
|
],
|
|
587
707
|
hints: [
|
|
588
708
|
"Use this tool before the List Errors or Get Error tools to understand available filters",
|
|
589
|
-
"Look for display_id field in the response - these are the field names to use in filters"
|
|
590
|
-
]
|
|
709
|
+
"Look for display_id field in the response - these are the field names to use in filters",
|
|
710
|
+
],
|
|
591
711
|
}, async (_args, _extra) => {
|
|
592
712
|
const projectFields = this.cache.get(cacheKeys.CURRENT_PROJECT_EVENT_FILTERS);
|
|
593
713
|
if (!projectFields)
|
|
@@ -603,52 +723,54 @@ export class BugsnagClient {
|
|
|
603
723
|
useCases: [
|
|
604
724
|
"Mark an error as open, fixed or ignored",
|
|
605
725
|
"Discard or un-discard an error",
|
|
606
|
-
"Update the severity of an error"
|
|
726
|
+
"Update the severity of an error",
|
|
607
727
|
],
|
|
608
728
|
parameters: [
|
|
609
|
-
...(this.projectApiKey
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
729
|
+
...(this.projectApiKey
|
|
730
|
+
? []
|
|
731
|
+
: [
|
|
732
|
+
{
|
|
733
|
+
name: "projectId",
|
|
734
|
+
type: z.string(),
|
|
735
|
+
description: "ID of the project that contains the error to be updated",
|
|
736
|
+
required: true,
|
|
737
|
+
},
|
|
738
|
+
]),
|
|
617
739
|
{
|
|
618
740
|
name: "errorId",
|
|
619
741
|
type: z.string(),
|
|
620
742
|
description: "ID of the error to update",
|
|
621
743
|
required: true,
|
|
622
|
-
examples: ["6863e2af8c857c0a5023b411"]
|
|
744
|
+
examples: ["6863e2af8c857c0a5023b411"],
|
|
623
745
|
},
|
|
624
746
|
{
|
|
625
747
|
name: "operation",
|
|
626
748
|
type: z.enum(PERMITTED_UPDATE_OPERATIONS),
|
|
627
749
|
description: "The operation to apply to the error",
|
|
628
750
|
required: true,
|
|
629
|
-
examples: ["fix", "open", "ignore", "discard", "undiscard"]
|
|
630
|
-
}
|
|
751
|
+
examples: ["fix", "open", "ignore", "discard", "undiscard"],
|
|
752
|
+
},
|
|
631
753
|
],
|
|
632
754
|
examples: [
|
|
633
755
|
{
|
|
634
756
|
description: "Mark an error as fixed",
|
|
635
757
|
parameters: {
|
|
636
758
|
errorId: "6863e2af8c857c0a5023b411",
|
|
637
|
-
operation: "fix"
|
|
759
|
+
operation: "fix",
|
|
638
760
|
},
|
|
639
|
-
expectedOutput: "Success response indicating the error was marked as fixed"
|
|
640
|
-
}
|
|
761
|
+
expectedOutput: "Success response indicating the error was marked as fixed",
|
|
762
|
+
},
|
|
641
763
|
],
|
|
642
764
|
hints: [
|
|
643
|
-
"Only use valid operations - BugSnag may reject invalid values"
|
|
765
|
+
"Only use valid operations - BugSnag may reject invalid values",
|
|
644
766
|
],
|
|
645
767
|
readOnly: false,
|
|
646
768
|
idempotent: false,
|
|
647
769
|
}, async (args, _extra) => {
|
|
648
770
|
const { errorId, operation } = args;
|
|
649
771
|
const project = await this.getInputProject(args.projectId);
|
|
650
|
-
let severity
|
|
651
|
-
if (operation ===
|
|
772
|
+
let severity;
|
|
773
|
+
if (operation === "override_severity") {
|
|
652
774
|
// illicit the severity from the user
|
|
653
775
|
const result = await getInput({
|
|
654
776
|
message: "Please provide the new severity for the error (e.g. 'info', 'warning', 'error', 'critical')",
|
|
@@ -657,30 +779,347 @@ export class BugsnagClient {
|
|
|
657
779
|
properties: {
|
|
658
780
|
severity: {
|
|
659
781
|
type: "string",
|
|
660
|
-
enum: [
|
|
661
|
-
description: "The new severity level for the error"
|
|
662
|
-
}
|
|
663
|
-
}
|
|
782
|
+
enum: ["info", "warning", "error"],
|
|
783
|
+
description: "The new severity level for the error",
|
|
784
|
+
},
|
|
785
|
+
},
|
|
664
786
|
},
|
|
665
|
-
required: ["severity"]
|
|
787
|
+
required: ["severity"],
|
|
666
788
|
});
|
|
667
789
|
if (result.action === "accept" && result.content?.severity) {
|
|
668
790
|
severity = result.content.severity;
|
|
669
791
|
}
|
|
670
792
|
}
|
|
671
|
-
const result = await this.updateError(project.id, errorId, operation, {
|
|
793
|
+
const result = await this.updateError(project.id, errorId, operation, {
|
|
794
|
+
severity,
|
|
795
|
+
});
|
|
796
|
+
return {
|
|
797
|
+
content: [
|
|
798
|
+
{ type: "text", text: JSON.stringify({ success: result }) },
|
|
799
|
+
],
|
|
800
|
+
};
|
|
801
|
+
});
|
|
802
|
+
register({
|
|
803
|
+
title: "List Builds",
|
|
804
|
+
summary: "List builds for a project with optional filtering by release stage",
|
|
805
|
+
purpose: "Retrieve a list of build summaries to analyze deployment history and associated errors",
|
|
806
|
+
useCases: [
|
|
807
|
+
"View recent builds to correlate with error spikes",
|
|
808
|
+
"Filter builds by stage (e.g. production, staging) for targeted analysis",
|
|
809
|
+
],
|
|
810
|
+
parameters: [
|
|
811
|
+
...(this.projectApiKey
|
|
812
|
+
? []
|
|
813
|
+
: [
|
|
814
|
+
{
|
|
815
|
+
name: "projectId",
|
|
816
|
+
type: z.string(),
|
|
817
|
+
description: "ID of the project to list builds for",
|
|
818
|
+
required: true,
|
|
819
|
+
},
|
|
820
|
+
]),
|
|
821
|
+
{
|
|
822
|
+
name: "releaseStage",
|
|
823
|
+
type: z.string(),
|
|
824
|
+
description: "Filter builds by this stage (e.g. production, staging)",
|
|
825
|
+
required: false,
|
|
826
|
+
examples: ["production", "staging"],
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
name: "nextUrl",
|
|
830
|
+
type: z.string(),
|
|
831
|
+
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.",
|
|
832
|
+
required: false,
|
|
833
|
+
examples: [
|
|
834
|
+
"/projects/515fb9337c1074f6fd000003/builds?offset=30&per_page=30",
|
|
835
|
+
],
|
|
836
|
+
},
|
|
837
|
+
],
|
|
838
|
+
examples: [
|
|
839
|
+
{
|
|
840
|
+
description: "List all builds for a project",
|
|
841
|
+
parameters: {},
|
|
842
|
+
expectedOutput: "JSON array of build objects with metadata",
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
description: "List production builds for a project",
|
|
846
|
+
parameters: {
|
|
847
|
+
releaseStage: "production",
|
|
848
|
+
},
|
|
849
|
+
expectedOutput: "JSON array of build objects in the production stage",
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
description: "Get the next page of results",
|
|
853
|
+
parameters: {
|
|
854
|
+
nextUrl: "/projects/515fb9337c1074f6fd000003/builds?offset=30&per_page=30",
|
|
855
|
+
},
|
|
856
|
+
expectedOutput: "JSON array of build objects with metadata from the next page",
|
|
857
|
+
},
|
|
858
|
+
],
|
|
859
|
+
hints: ["For more detailed results use the Get Build tool"],
|
|
860
|
+
readOnly: true,
|
|
861
|
+
idempotent: true,
|
|
862
|
+
outputFormat: "JSON array of build summary objects with metadata",
|
|
863
|
+
}, async (args, _extra) => {
|
|
864
|
+
const project = await this.getInputProject(args.projectId);
|
|
865
|
+
const { builds, nextUrl } = await this.listBuilds(project.id, {
|
|
866
|
+
release_stage: args.releaseStage,
|
|
867
|
+
next_url: args.nextUrl,
|
|
868
|
+
});
|
|
672
869
|
return {
|
|
673
|
-
content: [
|
|
870
|
+
content: [
|
|
871
|
+
{
|
|
872
|
+
type: "text",
|
|
873
|
+
text: JSON.stringify({
|
|
874
|
+
builds,
|
|
875
|
+
next: nextUrl,
|
|
876
|
+
}),
|
|
877
|
+
},
|
|
878
|
+
],
|
|
879
|
+
};
|
|
880
|
+
});
|
|
881
|
+
register({
|
|
882
|
+
title: "Get Build",
|
|
883
|
+
summary: "Get more details for a specific build by its ID",
|
|
884
|
+
purpose: "Retrieve detailed information about a build for analysis and debugging",
|
|
885
|
+
useCases: [
|
|
886
|
+
"View build metadata such as version, source control info, and error counts",
|
|
887
|
+
"Analyze a specific build to correlate with error spikes or deployments",
|
|
888
|
+
"See the stability targets for a project and if the build meets them",
|
|
889
|
+
],
|
|
890
|
+
parameters: [
|
|
891
|
+
...(this.projectApiKey
|
|
892
|
+
? []
|
|
893
|
+
: [
|
|
894
|
+
{
|
|
895
|
+
name: "projectId",
|
|
896
|
+
type: z.string(),
|
|
897
|
+
description: "ID of the project containing the build",
|
|
898
|
+
required: true,
|
|
899
|
+
},
|
|
900
|
+
]),
|
|
901
|
+
{
|
|
902
|
+
name: "buildId",
|
|
903
|
+
type: z.string(),
|
|
904
|
+
description: "ID of the build to retrieve",
|
|
905
|
+
required: true,
|
|
906
|
+
examples: ["5f8d0d55c9e77c0017a1b2c3"],
|
|
907
|
+
},
|
|
908
|
+
],
|
|
909
|
+
examples: [
|
|
910
|
+
{
|
|
911
|
+
description: "Get details for a specific build",
|
|
912
|
+
parameters: {
|
|
913
|
+
buildId: "5f8d0d55c9e77c0017a1b2c3",
|
|
914
|
+
},
|
|
915
|
+
expectedOutput: "JSON object with build details including version, source control info, error counts and stability data.",
|
|
916
|
+
},
|
|
917
|
+
],
|
|
918
|
+
hints: ["Build IDs can be found using the List builds tool"],
|
|
919
|
+
readOnly: true,
|
|
920
|
+
idempotent: true,
|
|
921
|
+
outputFormat: "JSON object containing build details along with stability metrics such as user and session stability, and whether it meets project targets",
|
|
922
|
+
}, async (args, _extra) => {
|
|
923
|
+
if (!args.buildId)
|
|
924
|
+
throw new Error("buildId argument is required");
|
|
925
|
+
const build = await this.getBuild((await this.getInputProject(args.projectId)).id, args.buildId);
|
|
926
|
+
return {
|
|
927
|
+
content: [{ type: "text", text: JSON.stringify(build) }],
|
|
928
|
+
};
|
|
929
|
+
});
|
|
930
|
+
register({
|
|
931
|
+
title: "List Releases",
|
|
932
|
+
summary: "List releases for a project with optional filtering by release stage",
|
|
933
|
+
purpose: "Retrieve a list of release summaries to analyze deployment history and associated errors",
|
|
934
|
+
useCases: [
|
|
935
|
+
"View recent releases to correlate with error spikes",
|
|
936
|
+
"Filter releases by stage (e.g. production, staging) for targeted analysis",
|
|
937
|
+
],
|
|
938
|
+
parameters: [
|
|
939
|
+
...(this.projectApiKey
|
|
940
|
+
? []
|
|
941
|
+
: [
|
|
942
|
+
{
|
|
943
|
+
name: "projectId",
|
|
944
|
+
type: z.string(),
|
|
945
|
+
description: "ID of the project to list releases for",
|
|
946
|
+
required: true,
|
|
947
|
+
},
|
|
948
|
+
]),
|
|
949
|
+
{
|
|
950
|
+
name: "releaseStage",
|
|
951
|
+
type: z.string(),
|
|
952
|
+
description: "Filter releases by this stage (e.g. production, staging)",
|
|
953
|
+
required: false,
|
|
954
|
+
examples: ["production", "staging"],
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
name: "visibleOnly",
|
|
958
|
+
type: z.boolean().default(true),
|
|
959
|
+
description: "Whether to only include releases that are marked as visible (default: true)",
|
|
960
|
+
required: true,
|
|
961
|
+
examples: ["true", "false"],
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
name: "nextUrl",
|
|
965
|
+
type: z.string(),
|
|
966
|
+
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.",
|
|
967
|
+
required: false,
|
|
968
|
+
examples: [
|
|
969
|
+
"/projects/515fb9337c1074f6fd000003/releases?offset=30&per_page=30",
|
|
970
|
+
],
|
|
971
|
+
},
|
|
972
|
+
],
|
|
973
|
+
examples: [
|
|
974
|
+
{
|
|
975
|
+
description: "List all releases for a project",
|
|
976
|
+
parameters: {},
|
|
977
|
+
expectedOutput: "JSON array of release objects with metadata",
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
description: "List production releases for a project",
|
|
981
|
+
parameters: {
|
|
982
|
+
releaseStage: "production",
|
|
983
|
+
},
|
|
984
|
+
expectedOutput: "JSON array of release objects in the production stage",
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
description: "Get the next page of results",
|
|
988
|
+
parameters: {
|
|
989
|
+
nextUrl: "/projects/515fb9337c1074f6fd000003/releases?offset=30&per_page=30",
|
|
990
|
+
},
|
|
991
|
+
expectedOutput: "JSON array of release objects with metadata from the next page",
|
|
992
|
+
},
|
|
993
|
+
],
|
|
994
|
+
hints: ["For more detailed results use the Get Release tool"],
|
|
995
|
+
readOnly: true,
|
|
996
|
+
idempotent: true,
|
|
997
|
+
outputFormat: "JSON array of release summary objects with metadata",
|
|
998
|
+
}, async (args, _extra) => {
|
|
999
|
+
const { releases, nextUrl } = await this.listReleases((await this.getInputProject(args.projectId)).id, {
|
|
1000
|
+
release_stage_name: args.releaseStage ?? "production",
|
|
1001
|
+
visible_only: args.visibleOnly,
|
|
1002
|
+
next_url: args.nextUrl ?? null,
|
|
1003
|
+
});
|
|
1004
|
+
return {
|
|
1005
|
+
content: [
|
|
1006
|
+
{
|
|
1007
|
+
type: "text",
|
|
1008
|
+
text: JSON.stringify({
|
|
1009
|
+
releases,
|
|
1010
|
+
next: nextUrl ?? null,
|
|
1011
|
+
}),
|
|
1012
|
+
},
|
|
1013
|
+
],
|
|
1014
|
+
};
|
|
1015
|
+
});
|
|
1016
|
+
register({
|
|
1017
|
+
title: "Get Release",
|
|
1018
|
+
summary: "Get more details for a specific release by its ID",
|
|
1019
|
+
purpose: "Retrieve detailed information about a release for analysis and debugging",
|
|
1020
|
+
useCases: [
|
|
1021
|
+
"View release metadata such as version, source control info, and error counts",
|
|
1022
|
+
"Analyze a specific release to correlate with error spikes or deployments",
|
|
1023
|
+
"See the stability targets for a project and if the release meets them",
|
|
1024
|
+
],
|
|
1025
|
+
parameters: [
|
|
1026
|
+
...(this.projectApiKey
|
|
1027
|
+
? []
|
|
1028
|
+
: [
|
|
1029
|
+
{
|
|
1030
|
+
name: "projectId",
|
|
1031
|
+
type: z.string(),
|
|
1032
|
+
description: "ID of the project containing the release",
|
|
1033
|
+
required: true,
|
|
1034
|
+
},
|
|
1035
|
+
]),
|
|
1036
|
+
{
|
|
1037
|
+
name: "releaseId",
|
|
1038
|
+
type: z.string(),
|
|
1039
|
+
description: "ID of the release to retrieve",
|
|
1040
|
+
required: true,
|
|
1041
|
+
examples: ["5f8d0d55c9e77c0017a1b2c3"],
|
|
1042
|
+
},
|
|
1043
|
+
],
|
|
1044
|
+
examples: [
|
|
1045
|
+
{
|
|
1046
|
+
description: "Get details for a specific release",
|
|
1047
|
+
parameters: {
|
|
1048
|
+
releaseId: "5f8d0d55c9e77c0017a1b2c3",
|
|
1049
|
+
},
|
|
1050
|
+
expectedOutput: "JSON object with release details including version, source control info, error counts and stability data.",
|
|
1051
|
+
},
|
|
1052
|
+
],
|
|
1053
|
+
hints: ["Release IDs can be found using the List releases tool"],
|
|
1054
|
+
readOnly: true,
|
|
1055
|
+
idempotent: true,
|
|
1056
|
+
outputFormat: "JSON object containing release details along with stability metrics such as user and session stability, and whether it meets project targets",
|
|
1057
|
+
}, async (args, _extra) => {
|
|
1058
|
+
if (!args.releaseId)
|
|
1059
|
+
throw new Error("releaseId argument is required");
|
|
1060
|
+
const release = await this.getRelease((await this.getInputProject(args.projectId)).id, args.releaseId);
|
|
1061
|
+
return {
|
|
1062
|
+
content: [{ type: "text", text: JSON.stringify(release) }],
|
|
1063
|
+
};
|
|
1064
|
+
});
|
|
1065
|
+
register({
|
|
1066
|
+
title: "List Builds in Release",
|
|
1067
|
+
summary: "List builds associated with a specific release",
|
|
1068
|
+
purpose: "Retrieve a list of builds for a given release to analyze deployment history and associated errors",
|
|
1069
|
+
useCases: [
|
|
1070
|
+
"View builds within a release to correlate with error spikes",
|
|
1071
|
+
"Analyze the composition of a release by examining its builds",
|
|
1072
|
+
],
|
|
1073
|
+
parameters: [
|
|
1074
|
+
...(this.projectApiKey
|
|
1075
|
+
? []
|
|
1076
|
+
: [
|
|
1077
|
+
{
|
|
1078
|
+
name: "projectId",
|
|
1079
|
+
type: z.string(),
|
|
1080
|
+
description: "ID of the project containing the release",
|
|
1081
|
+
required: true,
|
|
1082
|
+
},
|
|
1083
|
+
]),
|
|
1084
|
+
{
|
|
1085
|
+
name: "releaseId",
|
|
1086
|
+
type: z.string(),
|
|
1087
|
+
description: "ID of the release to list builds for",
|
|
1088
|
+
required: true,
|
|
1089
|
+
examples: ["5f8d0d55c9e77c0017a1b2c3"],
|
|
1090
|
+
},
|
|
1091
|
+
],
|
|
1092
|
+
examples: [
|
|
1093
|
+
{
|
|
1094
|
+
description: "List all builds in a specific release",
|
|
1095
|
+
parameters: {
|
|
1096
|
+
releaseId: "5f8d0d55c9e77c0017a1b2c3",
|
|
1097
|
+
},
|
|
1098
|
+
expectedOutput: "JSON array of build objects with metadata",
|
|
1099
|
+
},
|
|
1100
|
+
],
|
|
1101
|
+
hints: ["Release IDs can be found using the List releases tool"],
|
|
1102
|
+
readOnly: true,
|
|
1103
|
+
idempotent: true,
|
|
1104
|
+
outputFormat: "JSON array of build summary objects with metadata",
|
|
1105
|
+
}, async (args, _extra) => {
|
|
1106
|
+
if (!args.releaseId)
|
|
1107
|
+
throw new Error("releaseId argument is required");
|
|
1108
|
+
const builds = await this.listBuildsInRelease(args.releaseId);
|
|
1109
|
+
return {
|
|
1110
|
+
content: [{ type: "text", text: JSON.stringify(builds) }],
|
|
674
1111
|
};
|
|
675
1112
|
});
|
|
676
1113
|
}
|
|
677
1114
|
registerResources(register) {
|
|
678
1115
|
register("event", "{id}", async (uri, variables, _extra) => {
|
|
679
1116
|
return {
|
|
680
|
-
contents: [
|
|
1117
|
+
contents: [
|
|
1118
|
+
{
|
|
681
1119
|
uri: uri.href,
|
|
682
|
-
text: JSON.stringify(await this.getEvent(variables.id))
|
|
683
|
-
}
|
|
1120
|
+
text: JSON.stringify(await this.getEvent(variables.id)),
|
|
1121
|
+
},
|
|
1122
|
+
],
|
|
684
1123
|
};
|
|
685
1124
|
});
|
|
686
1125
|
}
|