@smartbear/mcp 0.10.0 → 0.12.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 +10 -8
- package/dist/bugsnag/client/api/Error.js +1 -1
- package/dist/bugsnag/client/api/Project.js +152 -0
- package/dist/bugsnag/client/api/api.js +130 -3
- package/dist/bugsnag/client/api/base.js +19 -0
- package/dist/bugsnag/client/api/index.js +2 -0
- package/dist/bugsnag/client/filters.js +0 -6
- package/dist/bugsnag/client.js +739 -378
- package/dist/bugsnag/input-schemas.js +59 -0
- package/dist/collaborator/client.js +18 -5
- package/dist/common/cache.js +63 -0
- package/dist/common/client-registry.js +128 -0
- package/dist/common/register-clients.js +31 -0
- package/dist/common/server.js +30 -9
- package/dist/common/transport-http.js +377 -0
- package/dist/common/transport-stdio.js +48 -0
- package/dist/index.js +20 -70
- package/dist/pactflow/client.js +39 -19
- package/dist/qmetry/client/auto-resolve.js +10 -0
- package/dist/qmetry/client/automation.js +171 -0
- package/dist/qmetry/client/handlers.js +9 -2
- package/dist/qmetry/client/project.js +87 -1
- package/dist/qmetry/client/testsuite.js +37 -1
- package/dist/qmetry/client/tools/automation-tools.js +290 -0
- package/dist/qmetry/client/tools/index.js +3 -0
- package/dist/qmetry/client/tools/issue-tools.js +1 -1
- package/dist/qmetry/client/tools/project-tools.js +311 -1
- package/dist/qmetry/client/tools/requirement-tools.js +1 -1
- package/dist/qmetry/client/tools/testcase-tools.js +311 -39
- package/dist/qmetry/client/tools/testsuite-tools.js +337 -23
- package/dist/qmetry/client.js +24 -9
- package/dist/qmetry/config/constants.js +6 -0
- package/dist/qmetry/config/rest-endpoints.js +8 -0
- package/dist/qmetry/types/automation.js +8 -0
- package/dist/qmetry/types/common.js +299 -4
- package/dist/qmetry/types/issues.js +5 -0
- package/dist/qmetry/types/project.js +13 -0
- package/dist/qmetry/types/requirements.js +5 -0
- package/dist/qmetry/types/testcase.js +5 -0
- package/dist/qmetry/types/testsuite.js +9 -0
- package/dist/reflect/client.js +10 -4
- package/dist/{api-hub → swagger}/client/api.js +94 -37
- package/dist/{api-hub → swagger}/client/configuration.js +4 -2
- package/dist/{api-hub → swagger}/client/index.js +2 -2
- package/dist/{api-hub → swagger}/client/portal-types.js +7 -6
- package/dist/{api-hub → swagger}/client/registry-types.js +26 -0
- package/dist/{api-hub → swagger}/client/tools.js +19 -20
- package/dist/{api-hub → swagger}/client.js +51 -39
- package/dist/swagger/config-utils.js +18 -0
- package/dist/tests/unit/bugsnag/utils/factories.js +86 -0
- package/dist/zephyr/client.js +44 -8
- package/dist/zephyr/common/rest-api-schemas.js +79 -78
- package/dist/zephyr/tool/environment/get-environments.js +68 -0
- package/dist/zephyr/tool/priority/get-priorities.js +43 -0
- package/dist/zephyr/tool/status/get-statuses.js +49 -0
- package/dist/zephyr/tool/test-case/get-test-case.js +39 -0
- package/dist/zephyr/tool/test-case/get-test-cases.js +64 -0
- package/dist/zephyr/tool/test-cycle/get-test-cycle.js +39 -0
- package/dist/zephyr/tool/test-cycle/get-test-cycles.js +2 -2
- package/dist/zephyr/tool/test-execution/get-test-execution.js +39 -0
- package/dist/zephyr/tool/test-execution/get-test-executions.js +45 -0
- package/package.json +4 -3
- /package/dist/{api-hub → swagger}/client/user-management-types.js +0 -0
package/dist/bugsnag/client.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import NodeCache from "node-cache";
|
|
2
1
|
import { z } from "zod";
|
|
3
2
|
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
|
|
4
3
|
import { ToolError, } from "../common/types.js";
|
|
5
|
-
import { Configuration, CurrentUserAPI, ErrorAPI, ProjectAPI, } from "./client/api/index.js";
|
|
6
|
-
import {
|
|
4
|
+
import { Configuration, CurrentUserAPI, ErrorAPI, ErrorUpdateRequest, ProjectAPI, } from "./client/api/index.js";
|
|
5
|
+
import { toUrlSearchParams } from "./client/filters.js";
|
|
6
|
+
import { toolInputParameters } from "./input-schemas.js";
|
|
7
7
|
const HUB_PREFIX = "00000";
|
|
8
8
|
const DEFAULT_DOMAIN = "bugsnag.com";
|
|
9
9
|
const HUB_DOMAIN = "bugsnag.smartbear.com";
|
|
10
10
|
const cacheKeys = {
|
|
11
11
|
ORG: "bugsnag_org",
|
|
12
12
|
PROJECTS: "bugsnag_projects",
|
|
13
|
+
PROJECT_EVENT_FIELDS: "bugsnag_project_event_fields",
|
|
14
|
+
PROJECT_TRACE_FIELDS: "bugsnag_project_trace_fields",
|
|
13
15
|
CURRENT_PROJECT: "bugsnag_current_project",
|
|
14
|
-
CURRENT_PROJECT_EVENT_FILTERS: "bugsnag_current_project_event_filters",
|
|
15
16
|
};
|
|
16
17
|
// Exclude certain event fields from the project event filters to improve agent usage
|
|
17
18
|
const EXCLUDED_EVENT_FIELDS = new Set([
|
|
@@ -25,57 +26,92 @@ const PERMITTED_UPDATE_OPERATIONS = [
|
|
|
25
26
|
"discard",
|
|
26
27
|
"undiscard",
|
|
27
28
|
];
|
|
29
|
+
const ConfigurationSchema = z.object({
|
|
30
|
+
auth_token: z.string().describe("BugSnag personal authentication token"),
|
|
31
|
+
project_api_key: z.string().describe("BugSnag project API key").optional(),
|
|
32
|
+
endpoint: z.string().url().describe("BugSnag endpoint URL").optional(),
|
|
33
|
+
});
|
|
28
34
|
export class BugsnagClient {
|
|
29
|
-
currentUserApi;
|
|
30
|
-
errorsApi;
|
|
31
35
|
cache;
|
|
32
|
-
projectApi;
|
|
33
36
|
projectApiKey;
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
configuredProjectApiKey;
|
|
38
|
+
_currentUserApi;
|
|
39
|
+
_errorsApi;
|
|
40
|
+
_projectApi;
|
|
41
|
+
_appEndpoint;
|
|
42
|
+
get currentUserApi() {
|
|
43
|
+
if (!this._currentUserApi)
|
|
44
|
+
throw new Error("Client not configured");
|
|
45
|
+
return this._currentUserApi;
|
|
46
|
+
}
|
|
47
|
+
get errorsApi() {
|
|
48
|
+
if (!this._errorsApi)
|
|
49
|
+
throw new Error("Client not configured");
|
|
50
|
+
return this._errorsApi;
|
|
51
|
+
}
|
|
52
|
+
get projectApi() {
|
|
53
|
+
if (!this._projectApi)
|
|
54
|
+
throw new Error("Client not configured");
|
|
55
|
+
return this._projectApi;
|
|
56
|
+
}
|
|
57
|
+
get appEndpoint() {
|
|
58
|
+
if (!this._appEndpoint)
|
|
59
|
+
throw new Error("Client not configured");
|
|
60
|
+
return this._appEndpoint;
|
|
61
|
+
}
|
|
36
62
|
name = "BugSnag";
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
toolPrefix = "bugsnag";
|
|
64
|
+
configPrefix = "Bugsnag";
|
|
65
|
+
config = ConfigurationSchema;
|
|
66
|
+
async configure(server, config) {
|
|
67
|
+
this.cache = server.getCache();
|
|
68
|
+
this._appEndpoint = this.getEndpoint("app", config.project_api_key, config.endpoint);
|
|
69
|
+
const apiConfig = new Configuration({
|
|
70
|
+
apiKey: `token ${config.auth_token}`,
|
|
43
71
|
headers: {
|
|
44
72
|
"User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`,
|
|
45
73
|
"Content-Type": "application/json",
|
|
46
74
|
"X-Bugsnag-API": "true",
|
|
47
75
|
"X-Version": "2",
|
|
48
76
|
},
|
|
49
|
-
basePath: this.
|
|
50
|
-
});
|
|
51
|
-
this.currentUserApi = new CurrentUserAPI(config);
|
|
52
|
-
this.errorsApi = new ErrorAPI(config);
|
|
53
|
-
this.cache = new NodeCache({
|
|
54
|
-
stdTTL: 24 * 60 * 60, // default cache TTL of 24 hours
|
|
77
|
+
basePath: this.getEndpoint("api", config.project_api_key, config.endpoint),
|
|
55
78
|
});
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
79
|
+
this._currentUserApi = new CurrentUserAPI(apiConfig);
|
|
80
|
+
this._errorsApi = new ErrorAPI(apiConfig);
|
|
81
|
+
this._projectApi = new ProjectAPI(apiConfig);
|
|
82
|
+
this.projectApiKey = config.project_api_key;
|
|
60
83
|
// Trigger caching of org and projects
|
|
61
84
|
try {
|
|
62
|
-
await this.getProjects();
|
|
85
|
+
const projects = await this.getProjects();
|
|
86
|
+
// If there's just one project, make this the current project
|
|
87
|
+
if (projects.length === 1 && !this.projectApiKey) {
|
|
88
|
+
this.projectApiKey = projects[0].apiKey;
|
|
89
|
+
}
|
|
63
90
|
}
|
|
64
91
|
catch (error) {
|
|
65
92
|
// Swallow auth errors here to allow the tools to be registered for visibility, even if the token is invalid
|
|
66
93
|
console.error("Unable to connect to BugSnag APIs, the BugSnag tools will not work. Check your configured BugSnag auth token.", error);
|
|
67
94
|
}
|
|
68
95
|
if (this.projectApiKey) {
|
|
96
|
+
this.configuredProjectApiKey = this.projectApiKey; // Store the originally configured API key
|
|
97
|
+
let currentProject = null;
|
|
69
98
|
try {
|
|
70
|
-
await this.getCurrentProject();
|
|
99
|
+
currentProject = await this.getCurrentProject();
|
|
71
100
|
}
|
|
72
101
|
catch (error) {
|
|
102
|
+
console.error("An error occurred while fetching project information", error);
|
|
103
|
+
}
|
|
104
|
+
if (currentProject) {
|
|
105
|
+
await this.getProjectEventFields(currentProject);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
73
108
|
// Clear the project API key to allow tools to work across all projects
|
|
74
109
|
this.projectApiKey = undefined;
|
|
75
110
|
console.error("Unable to find your configured BugSnag project, the BugSnag tools will continue to work across all projects in your organization. " +
|
|
76
|
-
"Check your configured BugSnag project API key."
|
|
111
|
+
"Check your configured BugSnag project API key.");
|
|
77
112
|
}
|
|
78
113
|
}
|
|
114
|
+
return true;
|
|
79
115
|
}
|
|
80
116
|
getHost(apiKey, subdomain) {
|
|
81
117
|
if (apiKey?.startsWith(HUB_PREFIX)) {
|
|
@@ -126,7 +162,7 @@ export class BugsnagClient {
|
|
|
126
162
|
return `${dashboardUrl}/errors/${errorId}${queryString ? `?${queryString}` : ""}`;
|
|
127
163
|
}
|
|
128
164
|
async getOrganization() {
|
|
129
|
-
let org = this.cache
|
|
165
|
+
let org = this.cache?.get(cacheKeys.ORG);
|
|
130
166
|
if (!org) {
|
|
131
167
|
const response = await this.currentUserApi.listUserOrganizations();
|
|
132
168
|
const orgs = response.body;
|
|
@@ -134,7 +170,7 @@ export class BugsnagClient {
|
|
|
134
170
|
throw new Error("No organizations found for the current user.");
|
|
135
171
|
}
|
|
136
172
|
org = orgs[0];
|
|
137
|
-
this.cache
|
|
173
|
+
this.cache?.set(cacheKeys.ORG, org);
|
|
138
174
|
}
|
|
139
175
|
return org;
|
|
140
176
|
}
|
|
@@ -143,12 +179,12 @@ export class BugsnagClient {
|
|
|
143
179
|
// stores them in the cache for future use.
|
|
144
180
|
// It throws an error if no organizations are found in the cache.
|
|
145
181
|
async getProjects() {
|
|
146
|
-
let projects = this.cache
|
|
182
|
+
let projects = this.cache?.get(cacheKeys.PROJECTS);
|
|
147
183
|
if (!projects) {
|
|
148
184
|
const org = await this.getOrganization();
|
|
149
185
|
const response = await this.currentUserApi.getOrganizationProjects(org.id);
|
|
150
186
|
projects = response.body;
|
|
151
|
-
this.cache
|
|
187
|
+
this.cache?.set(cacheKeys.PROJECTS, projects);
|
|
152
188
|
}
|
|
153
189
|
return projects;
|
|
154
190
|
}
|
|
@@ -157,28 +193,39 @@ export class BugsnagClient {
|
|
|
157
193
|
return projects.find((p) => p.id === projectId) || null;
|
|
158
194
|
}
|
|
159
195
|
async getCurrentProject() {
|
|
160
|
-
let project = this.cache
|
|
196
|
+
let project = this.cache?.get(cacheKeys.CURRENT_PROJECT) ?? null;
|
|
161
197
|
if (!project && this.projectApiKey) {
|
|
162
198
|
const projects = await this.getProjects();
|
|
163
199
|
project =
|
|
164
200
|
projects.find((p) => p.apiKey === this.projectApiKey) ?? null;
|
|
165
|
-
|
|
166
|
-
throw new ToolError("Unable to find project with the configured API key.");
|
|
167
|
-
}
|
|
168
|
-
this.cache.set(cacheKeys.CURRENT_PROJECT, project);
|
|
169
|
-
if (project) {
|
|
170
|
-
this.cache.set(cacheKeys.CURRENT_PROJECT_EVENT_FILTERS, await this.getProjectEventFilters(project));
|
|
171
|
-
}
|
|
201
|
+
this.cache?.set(cacheKeys.CURRENT_PROJECT, project);
|
|
172
202
|
}
|
|
173
203
|
return project;
|
|
174
204
|
}
|
|
175
|
-
async
|
|
176
|
-
|
|
177
|
-
if (!
|
|
178
|
-
|
|
205
|
+
async getProjectEventFields(project) {
|
|
206
|
+
const projectFiltersCache = this.cache?.get(cacheKeys.PROJECT_EVENT_FIELDS) || {};
|
|
207
|
+
if (!projectFiltersCache[project.id]) {
|
|
208
|
+
let filtersResponse = (await this.projectApi.listProjectEventFields(project.id)).body;
|
|
209
|
+
if (!filtersResponse || filtersResponse.length === 0) {
|
|
210
|
+
throw new ToolError(`No event fields found for project ${project.name}.`);
|
|
211
|
+
}
|
|
212
|
+
filtersResponse = filtersResponse.filter((field) => field.displayId && !EXCLUDED_EVENT_FIELDS.has(field.displayId));
|
|
213
|
+
projectFiltersCache[project.id] = filtersResponse;
|
|
214
|
+
this.cache?.set(cacheKeys.PROJECT_EVENT_FIELDS, projectFiltersCache);
|
|
215
|
+
}
|
|
216
|
+
return projectFiltersCache[project.id];
|
|
217
|
+
}
|
|
218
|
+
async getProjectTraceFields(project) {
|
|
219
|
+
const projectFiltersCache = this.cache?.get(cacheKeys.PROJECT_TRACE_FIELDS) || {};
|
|
220
|
+
if (!projectFiltersCache[project.id]) {
|
|
221
|
+
const filtersResponse = (await this.projectApi.listProjectTraceFields(project.id)).body;
|
|
222
|
+
if (!filtersResponse || filtersResponse.length === 0) {
|
|
223
|
+
throw new ToolError(`No trace fields found for project ${project.name}.`);
|
|
224
|
+
}
|
|
225
|
+
projectFiltersCache[project.id] = filtersResponse;
|
|
226
|
+
this.cache?.set(cacheKeys.PROJECT_TRACE_FIELDS, projectFiltersCache);
|
|
179
227
|
}
|
|
180
|
-
|
|
181
|
-
return filtersResponse;
|
|
228
|
+
return projectFiltersCache[project.id];
|
|
182
229
|
}
|
|
183
230
|
async getEvent(eventId, projectId) {
|
|
184
231
|
const projectIds = projectId
|
|
@@ -193,6 +240,10 @@ export class BugsnagClient {
|
|
|
193
240
|
if (!maybeProject) {
|
|
194
241
|
throw new ToolError(`Project with ID ${projectId} not found.`);
|
|
195
242
|
}
|
|
243
|
+
// If this hasn't been configured at startup, set this to the current project for future tool calls
|
|
244
|
+
if (!this.configuredProjectApiKey) {
|
|
245
|
+
this.cache?.set(cacheKeys.CURRENT_PROJECT, maybeProject);
|
|
246
|
+
}
|
|
196
247
|
return maybeProject;
|
|
197
248
|
}
|
|
198
249
|
else {
|
|
@@ -232,72 +283,69 @@ export class BugsnagClient {
|
|
|
232
283
|
};
|
|
233
284
|
}
|
|
234
285
|
registerTools(register, getInput) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
};
|
|
299
|
-
});
|
|
300
|
-
}
|
|
286
|
+
register({
|
|
287
|
+
title: "Get Current Project",
|
|
288
|
+
summary: "Retrieve the 'current' project on which tools should operate by default. This allows BugSnag tools to be called with no projectId parameter.",
|
|
289
|
+
purpose: "Gets information about the 'current' BugSnag project, including ID and API key",
|
|
290
|
+
useCases: ["Understand if a current project has been set"],
|
|
291
|
+
inputSchema: toolInputParameters.empty,
|
|
292
|
+
hints: [
|
|
293
|
+
"If a project is returned, it can be assumed that the user expects interactions with BugSnag tools to refer to this project",
|
|
294
|
+
"If this tool returns no current project then other BugSnag tools will require an explicit project ID parameter",
|
|
295
|
+
"Call the List Projects tool to see all projects that the user has access to. Get the project ID from this list either by asking the user for the project name or slug",
|
|
296
|
+
"You might find a BugSnag API key in the user's code where they configure the BugSnag SDK that can be matched to a project 'apiKey' field from the project list",
|
|
297
|
+
],
|
|
298
|
+
}, async (_args, _extra) => {
|
|
299
|
+
const project = await this.getCurrentProject();
|
|
300
|
+
if (!project) {
|
|
301
|
+
throw new ToolError("No current project is configured in the MCP server - use List Projects to see the available projects and use the project ID as a parameter to other BugSnag tools. You can ask the user to select the project based on the name or slug, or use the apiKey field and see if there's a BugSnag API key set in the user's code when they configure the BugSnag SDK");
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
content: [{ type: "text", text: JSON.stringify(project) }],
|
|
305
|
+
};
|
|
306
|
+
});
|
|
307
|
+
const listProjectsInputSchema = z.object({
|
|
308
|
+
apiKey: z
|
|
309
|
+
.string()
|
|
310
|
+
.optional()
|
|
311
|
+
.describe("The API key of the BugSnag project, if known."),
|
|
312
|
+
});
|
|
313
|
+
register({
|
|
314
|
+
title: "List Projects",
|
|
315
|
+
summary: "List all projects in the organization that the current user has access to, or find a project matching an API key.",
|
|
316
|
+
purpose: "Retrieve available projects for browsing and selecting which project to analyze.",
|
|
317
|
+
useCases: [
|
|
318
|
+
"Get an overview of all projects in the organization",
|
|
319
|
+
"Locate a project by its API key if known from the user's code",
|
|
320
|
+
],
|
|
321
|
+
inputSchema: listProjectsInputSchema,
|
|
322
|
+
hints: [
|
|
323
|
+
"Project IDs from this list can be used with other tools when no project API key is configured",
|
|
324
|
+
],
|
|
325
|
+
}, async (args, _extra) => {
|
|
326
|
+
const params = listProjectsInputSchema.parse(args);
|
|
327
|
+
let projects = await this.getProjects();
|
|
328
|
+
if (!projects || projects.length === 0) {
|
|
329
|
+
throw new ToolError("No BugSnag projects found for the current user.");
|
|
330
|
+
}
|
|
331
|
+
if (params.apiKey) {
|
|
332
|
+
const matchedProject = projects.find((p) => p.apiKey === params.apiKey);
|
|
333
|
+
projects = matchedProject ? [matchedProject] : [];
|
|
334
|
+
}
|
|
335
|
+
const content = {
|
|
336
|
+
data: projects,
|
|
337
|
+
count: projects.length,
|
|
338
|
+
};
|
|
339
|
+
return {
|
|
340
|
+
content: [{ type: "text", text: JSON.stringify(content) }],
|
|
341
|
+
};
|
|
342
|
+
});
|
|
343
|
+
const getErrorInputSchema = z.object({
|
|
344
|
+
projectId: toolInputParameters.projectId,
|
|
345
|
+
errorId: toolInputParameters.errorId.describe("Unique identifier of the error to retrieve"),
|
|
346
|
+
filters: toolInputParameters.filters.describe("Apply filters to narrow down the error list. Use the List Project Event Filters tool to discover available filter fields. " +
|
|
347
|
+
"Time filters support extended ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h)."),
|
|
348
|
+
});
|
|
301
349
|
register({
|
|
302
350
|
title: "Get Error",
|
|
303
351
|
summary: "Get full details on an error, including aggregated and summarized data across all events (occurrences) and details of the latest event (occurrence), such as breadcrumbs, metadata and the stacktrace. Use the filters parameter to narrow down the summaries further.",
|
|
@@ -308,42 +356,7 @@ export class BugsnagClient {
|
|
|
308
356
|
"Get error details for debugging and root cause analysis",
|
|
309
357
|
"Retrieve error metadata for incident reports and documentation",
|
|
310
358
|
],
|
|
311
|
-
|
|
312
|
-
{
|
|
313
|
-
name: "errorId",
|
|
314
|
-
type: z.string(),
|
|
315
|
-
required: true,
|
|
316
|
-
description: "Unique identifier of the error to retrieve",
|
|
317
|
-
examples: ["6863e2af8c857c0a5023b411"],
|
|
318
|
-
},
|
|
319
|
-
...(this.projectApiKey
|
|
320
|
-
? []
|
|
321
|
-
: [
|
|
322
|
-
{
|
|
323
|
-
name: "projectId",
|
|
324
|
-
type: z.string(),
|
|
325
|
-
required: true,
|
|
326
|
-
description: "ID of the project containing the error",
|
|
327
|
-
},
|
|
328
|
-
]),
|
|
329
|
-
{
|
|
330
|
-
name: "filters",
|
|
331
|
-
type: FilterObjectSchema,
|
|
332
|
-
required: false,
|
|
333
|
-
description: "Apply filters to narrow down the error list. Use the List Project Event Filters tool to discover available filter fields",
|
|
334
|
-
examples: [
|
|
335
|
-
'{"error.status": [{"type": "eq", "value": "open"}]}',
|
|
336
|
-
'{"event.since": [{"type": "eq", "value": "7d"}]} // Relative time: last 7 days',
|
|
337
|
-
'{"event.since": [{"type": "eq", "value": "2018-05-20T00:00:00Z"}]} // ISO 8601 UTC format',
|
|
338
|
-
'{"user.email": [{"type": "eq", "value": "user@example.com"}]}',
|
|
339
|
-
],
|
|
340
|
-
constraints: [
|
|
341
|
-
"Time filters support ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h)",
|
|
342
|
-
"ISO 8601 times must be in UTC and use extended format",
|
|
343
|
-
"Relative time periods: h (hours), d (days)",
|
|
344
|
-
],
|
|
345
|
-
},
|
|
346
|
-
],
|
|
359
|
+
inputSchema: getErrorInputSchema,
|
|
347
360
|
outputDescription: "JSON object containing: " +
|
|
348
361
|
" - error_details: Aggregated data about the error, including first and last seen occurrence" +
|
|
349
362
|
" - latest_event: Detailed information about the most recent occurrence of the error, including stacktrace, breadcrumbs, user and context" +
|
|
@@ -361,19 +374,19 @@ export class BugsnagClient {
|
|
|
361
374
|
hints: [
|
|
362
375
|
"Error IDs can be found using the List Project Errors tool",
|
|
363
376
|
"Use this after filtering errors to get detailed information about specific errors",
|
|
377
|
+
"Use Get Event Details tool if you need detailed information about a specific event (occurrence) rather than the aggregated error",
|
|
364
378
|
"If you used a filter to get this error, you can pass the same filters here to restrict the results or apply further filters",
|
|
365
379
|
"The URL provided in the response points should be shown to the user in all cases as it allows them to view the error in the dashboard and perform further analysis",
|
|
366
380
|
],
|
|
367
381
|
}, async (args, _extra) => {
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const errorDetails = (await this.errorsApi.viewErrorOnProject(project.id, args.errorId)).body;
|
|
382
|
+
const params = getErrorInputSchema.parse(args);
|
|
383
|
+
const project = await this.getInputProject(params.projectId);
|
|
384
|
+
const errorDetails = (await this.errorsApi.viewErrorOnProject(project.id, params.errorId)).body;
|
|
372
385
|
if (!errorDetails) {
|
|
373
|
-
throw new ToolError(`Error with ID ${
|
|
386
|
+
throw new ToolError(`Error with ID ${params.errorId} not found in project ${project.id}.`);
|
|
374
387
|
}
|
|
375
388
|
const filters = {
|
|
376
|
-
error: [{ type: "eq", value:
|
|
389
|
+
error: [{ type: "eq", value: params.errorId }],
|
|
377
390
|
...args.filters,
|
|
378
391
|
};
|
|
379
392
|
// Get the latest event for this error using the events endpoint with filters
|
|
@@ -382,6 +395,7 @@ export class BugsnagClient {
|
|
|
382
395
|
const latestEvents = (await this.errorsApi.listEventsOnProject(project.id, null, "timestamp", "desc", 1, filters, true)).body;
|
|
383
396
|
if (latestEvents && latestEvents.length > 0) {
|
|
384
397
|
latestEvent = latestEvents[0];
|
|
398
|
+
latestEvent.threads = undefined; // Remove threads to reduce payload size
|
|
385
399
|
}
|
|
386
400
|
}
|
|
387
401
|
catch (e) {
|
|
@@ -398,8 +412,42 @@ export class BugsnagClient {
|
|
|
398
412
|
content: [{ type: "text", text: JSON.stringify(content) }],
|
|
399
413
|
};
|
|
400
414
|
});
|
|
415
|
+
const getEventInputSchema = z.object({
|
|
416
|
+
projectId: toolInputParameters.projectId,
|
|
417
|
+
eventId: toolInputParameters.eventId,
|
|
418
|
+
});
|
|
401
419
|
register({
|
|
402
|
-
title: "Get Event
|
|
420
|
+
title: "Get Event",
|
|
421
|
+
summary: "Get detailed information about a specific event",
|
|
422
|
+
purpose: "Retrieve event details directly from its ID",
|
|
423
|
+
useCases: [
|
|
424
|
+
"Get the full details of an event, including any thread stack traces",
|
|
425
|
+
],
|
|
426
|
+
inputSchema: getEventInputSchema,
|
|
427
|
+
examples: [
|
|
428
|
+
{
|
|
429
|
+
description: "Get event details of an event",
|
|
430
|
+
parameters: {
|
|
431
|
+
eventId: "6863e2af012caf1d5c320000",
|
|
432
|
+
},
|
|
433
|
+
expectedOutput: "JSON object with complete event details including stack trace (error trace and other threads, if present), metadata, and context",
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
}, async (args, _extra) => {
|
|
437
|
+
const params = getEventInputSchema.parse(args);
|
|
438
|
+
const project = await this.getInputProject(params.projectId);
|
|
439
|
+
const response = await this.getEvent(params.eventId, project.id);
|
|
440
|
+
return {
|
|
441
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
442
|
+
};
|
|
443
|
+
});
|
|
444
|
+
const getEventDetailsFromDashboardUrlInputSchema = z.object({
|
|
445
|
+
link: z
|
|
446
|
+
.string()
|
|
447
|
+
.describe("Full URL to the event details page in the BugSnag dashboard (web interface), containing project slug and event_id parameter."),
|
|
448
|
+
});
|
|
449
|
+
register({
|
|
450
|
+
title: "Get Event Details From Dashboard URL",
|
|
403
451
|
summary: "Get detailed information about a specific event using its dashboard URL",
|
|
404
452
|
purpose: "Retrieve event details directly from a dashboard URL for quick debugging",
|
|
405
453
|
useCases: [
|
|
@@ -407,20 +455,7 @@ export class BugsnagClient {
|
|
|
407
455
|
"Extract event information from shared links or browser URLs",
|
|
408
456
|
"Quick lookup of event details without needing separate project and event IDs",
|
|
409
457
|
],
|
|
410
|
-
|
|
411
|
-
{
|
|
412
|
-
name: "link",
|
|
413
|
-
type: z.string(),
|
|
414
|
-
description: "Full URL to the event details page in the BugSnag dashboard (web interface)",
|
|
415
|
-
required: true,
|
|
416
|
-
examples: [
|
|
417
|
-
"https://app.bugsnag.com/my-org/my-project/errors/6863e2af8c857c0a5023b411?event_id=6863e2af012caf1d5c320000",
|
|
418
|
-
],
|
|
419
|
-
constraints: [
|
|
420
|
-
"Must be a valid dashboard URL containing project slug and event_id parameter",
|
|
421
|
-
],
|
|
422
|
-
},
|
|
423
|
-
],
|
|
458
|
+
inputSchema: getEventDetailsFromDashboardUrlInputSchema,
|
|
424
459
|
examples: [
|
|
425
460
|
{
|
|
426
461
|
description: "Get event details from a dashboard URL",
|
|
@@ -435,9 +470,8 @@ export class BugsnagClient {
|
|
|
435
470
|
"This is useful when users share BugSnag dashboard URLs and you need to extract the event data",
|
|
436
471
|
],
|
|
437
472
|
}, async (args, _extra) => {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
const url = new URL(args.link);
|
|
473
|
+
const params = getEventDetailsFromDashboardUrlInputSchema.parse(args);
|
|
474
|
+
const url = new URL(params.link);
|
|
441
475
|
const eventId = url.searchParams.get("event_id");
|
|
442
476
|
const projectSlug = url.pathname.split("/")[2];
|
|
443
477
|
if (!projectSlug || !eventId)
|
|
@@ -453,6 +487,15 @@ export class BugsnagClient {
|
|
|
453
487
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
454
488
|
};
|
|
455
489
|
});
|
|
490
|
+
const listProjectErrorsInputSchema = z.object({
|
|
491
|
+
projectId: toolInputParameters.projectId,
|
|
492
|
+
filters: toolInputParameters.filters.describe("Apply filters to narrow down the error list. Use the List Project Event Filters tool to discover available filter fields. " +
|
|
493
|
+
"Time filters support extended ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h)."),
|
|
494
|
+
sort: toolInputParameters.sort,
|
|
495
|
+
direction: toolInputParameters.direction,
|
|
496
|
+
perPage: toolInputParameters.perPage,
|
|
497
|
+
nextUrl: toolInputParameters.nextUrl,
|
|
498
|
+
});
|
|
456
499
|
register({
|
|
457
500
|
title: "List Project Errors",
|
|
458
501
|
summary: "List and search errors in a project using customizable filters and pagination",
|
|
@@ -463,73 +506,7 @@ export class BugsnagClient {
|
|
|
463
506
|
"Monitor error trends over time using date range filters",
|
|
464
507
|
"Find errors affecting specific users or environments using metadata filters",
|
|
465
508
|
],
|
|
466
|
-
|
|
467
|
-
{
|
|
468
|
-
name: "filters",
|
|
469
|
-
type: FilterObjectSchema.default({
|
|
470
|
-
"event.since": [{ type: "eq", value: "30d" }],
|
|
471
|
-
"error.status": [{ type: "eq", value: "open" }],
|
|
472
|
-
}),
|
|
473
|
-
description: "Apply filters to narrow down the error list. Use the List Project Event Filters tool to discover available filter fields",
|
|
474
|
-
required: false,
|
|
475
|
-
examples: [
|
|
476
|
-
'{"error.status": [{"type": "eq", "value": "open"}]}',
|
|
477
|
-
'{"event.since": [{"type": "eq", "value": "7d"}]} // Relative time: last 7 days',
|
|
478
|
-
'{"event.since": [{"type": "eq", "value": "2018-05-20T00:00:00Z"}]} // ISO 8601 UTC format',
|
|
479
|
-
'{"user.email": [{"type": "eq", "value": "user@example.com"}]}',
|
|
480
|
-
],
|
|
481
|
-
constraints: [
|
|
482
|
-
"Time filters support ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h)",
|
|
483
|
-
"ISO 8601 times must be in UTC and use extended format",
|
|
484
|
-
"Relative time periods: h (hours), d (days)",
|
|
485
|
-
],
|
|
486
|
-
},
|
|
487
|
-
{
|
|
488
|
-
name: "sort",
|
|
489
|
-
type: z
|
|
490
|
-
.enum(["first_seen", "last_seen", "events", "users", "unsorted"])
|
|
491
|
-
.default("last_seen"),
|
|
492
|
-
description: "Field to sort the errors by",
|
|
493
|
-
required: false,
|
|
494
|
-
examples: ["last_seen"],
|
|
495
|
-
},
|
|
496
|
-
{
|
|
497
|
-
name: "direction",
|
|
498
|
-
type: z.enum(["asc", "desc"]).default("desc"),
|
|
499
|
-
description: "Sort direction for ordering results",
|
|
500
|
-
required: false,
|
|
501
|
-
examples: ["desc"],
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
name: "perPage",
|
|
505
|
-
type: z.number().min(1).max(100).default(30),
|
|
506
|
-
description: "How many results to return per page.",
|
|
507
|
-
required: false,
|
|
508
|
-
examples: ["30", "50", "100"],
|
|
509
|
-
},
|
|
510
|
-
{
|
|
511
|
-
name: "nextUrl",
|
|
512
|
-
type: z.string(),
|
|
513
|
-
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.",
|
|
514
|
-
required: false,
|
|
515
|
-
examples: [
|
|
516
|
-
"https://api.bugsnag.com/projects/515fb9337c1074f6fd000003/errors?offset=30&per_page=30&sort=last_seen",
|
|
517
|
-
],
|
|
518
|
-
constraints: [
|
|
519
|
-
"Only values provided in the output from this tool can be used. Do not attempt to construct it manually.",
|
|
520
|
-
],
|
|
521
|
-
},
|
|
522
|
-
...(this.projectApiKey
|
|
523
|
-
? []
|
|
524
|
-
: [
|
|
525
|
-
{
|
|
526
|
-
name: "projectId",
|
|
527
|
-
type: z.string(),
|
|
528
|
-
description: "ID of the project to query for errors",
|
|
529
|
-
required: true,
|
|
530
|
-
},
|
|
531
|
-
]),
|
|
532
|
-
],
|
|
509
|
+
inputSchema: listProjectErrorsInputSchema,
|
|
533
510
|
examples: [
|
|
534
511
|
{
|
|
535
512
|
description: "Find errors affecting a specific user in the last 24 hours",
|
|
@@ -564,7 +541,7 @@ export class BugsnagClient {
|
|
|
564
541
|
},
|
|
565
542
|
],
|
|
566
543
|
hints: [
|
|
567
|
-
"Use
|
|
544
|
+
"Use List Project Event Filters tool first to discover valid filter field names for your project",
|
|
568
545
|
"Combine multiple filters to narrow results - filters are applied with AND logic",
|
|
569
546
|
"For time filters: use relative format (7d, 24h) for recent periods or ISO 8601 UTC format (2018-05-20T00:00:00Z) for specific dates",
|
|
570
547
|
"Common time filters: event.since (from this time), event.before (until this time)",
|
|
@@ -575,12 +552,13 @@ export class BugsnagClient {
|
|
|
575
552
|
"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.",
|
|
576
553
|
],
|
|
577
554
|
}, async (args, _extra) => {
|
|
578
|
-
const
|
|
555
|
+
const params = listProjectErrorsInputSchema.parse(args);
|
|
556
|
+
const project = await this.getInputProject(params.projectId);
|
|
579
557
|
// Validate filter keys against cached event fields
|
|
580
|
-
if (
|
|
581
|
-
const eventFields = this.
|
|
558
|
+
if (params.filters) {
|
|
559
|
+
const eventFields = await this.getProjectEventFields(project);
|
|
582
560
|
const validKeys = new Set(eventFields.map((f) => f.displayId));
|
|
583
|
-
for (const key of Object.keys(
|
|
561
|
+
for (const key of Object.keys(params.filters)) {
|
|
584
562
|
if (!validKeys.has(key)) {
|
|
585
563
|
throw new ToolError(`Invalid filter key: ${key}`);
|
|
586
564
|
}
|
|
@@ -589,9 +567,9 @@ export class BugsnagClient {
|
|
|
589
567
|
const filters = {
|
|
590
568
|
"event.since": [{ type: "eq", value: "30d" }],
|
|
591
569
|
"error.status": [{ type: "eq", value: "open" }],
|
|
592
|
-
...
|
|
570
|
+
...params.filters,
|
|
593
571
|
};
|
|
594
|
-
const response = await this.errorsApi.listProjectErrors(project.id, null,
|
|
572
|
+
const response = await this.errorsApi.listProjectErrors(project.id, null, params.sort, params.direction, params.perPage, filters, params.nextUrl);
|
|
595
573
|
const result = {
|
|
596
574
|
data: response.body,
|
|
597
575
|
next_url: response.nextUrl ?? undefined,
|
|
@@ -602,16 +580,19 @@ export class BugsnagClient {
|
|
|
602
580
|
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
603
581
|
};
|
|
604
582
|
});
|
|
583
|
+
const listProjectEventFiltersInputSchema = z.object({
|
|
584
|
+
projectId: toolInputParameters.projectId,
|
|
585
|
+
});
|
|
605
586
|
register({
|
|
606
587
|
title: "List Project Event Filters",
|
|
607
|
-
summary: "Get available event filter fields for
|
|
588
|
+
summary: "Get available event filter fields for a project",
|
|
608
589
|
purpose: "Discover valid filter field names and options that can be used with the List Errors or Get Error tools",
|
|
609
590
|
useCases: [
|
|
610
591
|
"Discover what filter fields are available before searching for errors",
|
|
611
592
|
"Find the correct field names for filtering by user, environment, or custom metadata",
|
|
612
593
|
"Understand filter options and data types for building complex queries",
|
|
613
594
|
],
|
|
614
|
-
|
|
595
|
+
inputSchema: listProjectEventFiltersInputSchema,
|
|
615
596
|
examples: [
|
|
616
597
|
{
|
|
617
598
|
description: "Get all available filter fields",
|
|
@@ -623,14 +604,20 @@ export class BugsnagClient {
|
|
|
623
604
|
"Use this tool before the List Errors or Get Error tools to understand available filters",
|
|
624
605
|
"Look for display_id field in the response - these are the field names to use in filters",
|
|
625
606
|
],
|
|
626
|
-
}, async (
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
throw new ToolError("No event filters found in cache.");
|
|
607
|
+
}, async (args, _extra) => {
|
|
608
|
+
const params = listProjectEventFiltersInputSchema.parse(args);
|
|
609
|
+
const eventFilters = await this.getProjectEventFields(await this.getInputProject(params.projectId));
|
|
630
610
|
return {
|
|
631
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
611
|
+
content: [{ type: "text", text: JSON.stringify(eventFilters) }],
|
|
632
612
|
};
|
|
633
613
|
});
|
|
614
|
+
const updateErrorInputSchema = z.object({
|
|
615
|
+
projectId: toolInputParameters.projectId,
|
|
616
|
+
errorId: toolInputParameters.errorId,
|
|
617
|
+
operation: z
|
|
618
|
+
.enum(PERMITTED_UPDATE_OPERATIONS)
|
|
619
|
+
.describe("The operation to apply to the error"),
|
|
620
|
+
});
|
|
634
621
|
register({
|
|
635
622
|
title: "Update Error",
|
|
636
623
|
summary: "Update the status of an error",
|
|
@@ -640,32 +627,7 @@ export class BugsnagClient {
|
|
|
640
627
|
"Discard or un-discard an error",
|
|
641
628
|
"Update the severity of an error",
|
|
642
629
|
],
|
|
643
|
-
|
|
644
|
-
...(this.projectApiKey
|
|
645
|
-
? []
|
|
646
|
-
: [
|
|
647
|
-
{
|
|
648
|
-
name: "projectId",
|
|
649
|
-
type: z.string(),
|
|
650
|
-
description: "ID of the project that contains the error to be updated",
|
|
651
|
-
required: true,
|
|
652
|
-
},
|
|
653
|
-
]),
|
|
654
|
-
{
|
|
655
|
-
name: "errorId",
|
|
656
|
-
type: z.string(),
|
|
657
|
-
description: "ID of the error to update",
|
|
658
|
-
required: true,
|
|
659
|
-
examples: ["6863e2af8c857c0a5023b411"],
|
|
660
|
-
},
|
|
661
|
-
{
|
|
662
|
-
name: "operation",
|
|
663
|
-
type: z.enum(PERMITTED_UPDATE_OPERATIONS),
|
|
664
|
-
description: "The operation to apply to the error",
|
|
665
|
-
required: true,
|
|
666
|
-
examples: ["fix", "open", "ignore", "discard", "undiscard"],
|
|
667
|
-
},
|
|
668
|
-
],
|
|
630
|
+
inputSchema: updateErrorInputSchema,
|
|
669
631
|
examples: [
|
|
670
632
|
{
|
|
671
633
|
description: "Mark an error as fixed",
|
|
@@ -682,10 +644,10 @@ export class BugsnagClient {
|
|
|
682
644
|
readOnly: false,
|
|
683
645
|
idempotent: false,
|
|
684
646
|
}, async (args, _extra) => {
|
|
685
|
-
const
|
|
686
|
-
const project = await this.getInputProject(
|
|
647
|
+
const params = updateErrorInputSchema.parse(args);
|
|
648
|
+
const project = await this.getInputProject(params.projectId);
|
|
687
649
|
let severity;
|
|
688
|
-
if (operation === "override_severity") {
|
|
650
|
+
if (params.operation === "override_severity") {
|
|
689
651
|
// illicit the severity from the user
|
|
690
652
|
const result = await getInput({
|
|
691
653
|
message: "Please provide the new severity for the error (e.g. 'info', 'warning', 'error', 'critical')",
|
|
@@ -705,8 +667,8 @@ export class BugsnagClient {
|
|
|
705
667
|
severity = result.content.severity;
|
|
706
668
|
}
|
|
707
669
|
}
|
|
708
|
-
const result = await this.errorsApi.updateErrorOnProject(project.id, errorId, {
|
|
709
|
-
operation: operation,
|
|
670
|
+
const result = await this.errorsApi.updateErrorOnProject(project.id, params.errorId, {
|
|
671
|
+
operation: Object.values(ErrorUpdateRequest.OperationEnum).find((value) => value === params.operation),
|
|
710
672
|
severity: severity,
|
|
711
673
|
});
|
|
712
674
|
return {
|
|
@@ -720,6 +682,16 @@ export class BugsnagClient {
|
|
|
720
682
|
],
|
|
721
683
|
};
|
|
722
684
|
});
|
|
685
|
+
const listReleasesInputSchema = z.object({
|
|
686
|
+
projectId: toolInputParameters.projectId,
|
|
687
|
+
releaseStage: toolInputParameters.releaseStage,
|
|
688
|
+
visibleOnly: z
|
|
689
|
+
.boolean()
|
|
690
|
+
.describe("Whether to only include releases that are marked as visible in the dashboard")
|
|
691
|
+
.default(false),
|
|
692
|
+
perPage: toolInputParameters.perPage,
|
|
693
|
+
nextUrl: toolInputParameters.nextUrl,
|
|
694
|
+
});
|
|
723
695
|
register({
|
|
724
696
|
title: "List Releases",
|
|
725
697
|
summary: "List releases for a project",
|
|
@@ -728,48 +700,7 @@ export class BugsnagClient {
|
|
|
728
700
|
"View recent releases to correlate with error spikes",
|
|
729
701
|
"Filter releases by stage (e.g. production, staging) for targeted analysis",
|
|
730
702
|
],
|
|
731
|
-
|
|
732
|
-
...(this.projectApiKey
|
|
733
|
-
? []
|
|
734
|
-
: [
|
|
735
|
-
{
|
|
736
|
-
name: "projectId",
|
|
737
|
-
type: z.string(),
|
|
738
|
-
description: "ID of the project to list releases for",
|
|
739
|
-
required: true,
|
|
740
|
-
},
|
|
741
|
-
]),
|
|
742
|
-
{
|
|
743
|
-
name: "releaseStage",
|
|
744
|
-
type: z.string().default("production"),
|
|
745
|
-
description: "Filter releases by this stage (e.g. production, staging), defaults to 'production'",
|
|
746
|
-
required: false,
|
|
747
|
-
examples: ["production", "staging"],
|
|
748
|
-
},
|
|
749
|
-
{
|
|
750
|
-
name: "visibleOnly",
|
|
751
|
-
type: z.boolean().default(false),
|
|
752
|
-
description: "Whether to only include releases that are marked as visible in the dashboard, defaults to false",
|
|
753
|
-
required: false,
|
|
754
|
-
examples: ["true", "false"],
|
|
755
|
-
},
|
|
756
|
-
{
|
|
757
|
-
name: "perPage",
|
|
758
|
-
type: z.number().min(1).max(100).default(30),
|
|
759
|
-
description: "How many results to return per page.",
|
|
760
|
-
required: false,
|
|
761
|
-
examples: ["30", "50", "100"],
|
|
762
|
-
},
|
|
763
|
-
{
|
|
764
|
-
name: "nextUrl",
|
|
765
|
-
type: z.string(),
|
|
766
|
-
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.",
|
|
767
|
-
required: false,
|
|
768
|
-
examples: [
|
|
769
|
-
"/projects/515fb9337c1074f6fd000003/releases?offset=30&per_page=30",
|
|
770
|
-
],
|
|
771
|
-
},
|
|
772
|
-
],
|
|
703
|
+
inputSchema: listReleasesInputSchema,
|
|
773
704
|
examples: [
|
|
774
705
|
{
|
|
775
706
|
description: "List production releases for a project",
|
|
@@ -800,9 +731,10 @@ export class BugsnagClient {
|
|
|
800
731
|
idempotent: true,
|
|
801
732
|
outputDescription: "JSON array of release summary objects with metadata, with a URL to the next page if more results are available",
|
|
802
733
|
}, async (args, _extra) => {
|
|
803
|
-
const
|
|
804
|
-
const
|
|
805
|
-
|
|
734
|
+
const params = listReleasesInputSchema.parse(args);
|
|
735
|
+
const project = await this.getInputProject(params.projectId);
|
|
736
|
+
const response = await this.projectApi.listProjectReleaseGroups(project.id, params.releaseStage, false, // Not top-only
|
|
737
|
+
params.visibleOnly, params.perPage, params.nextUrl);
|
|
806
738
|
let releases = [];
|
|
807
739
|
if (response.body) {
|
|
808
740
|
releases = response.body.map((r) => this.addStabilityData(r, project));
|
|
@@ -821,6 +753,10 @@ export class BugsnagClient {
|
|
|
821
753
|
],
|
|
822
754
|
};
|
|
823
755
|
});
|
|
756
|
+
const getReleaseInputSchema = z.object({
|
|
757
|
+
projectId: toolInputParameters.projectId,
|
|
758
|
+
releaseId: toolInputParameters.releaseId,
|
|
759
|
+
});
|
|
824
760
|
register({
|
|
825
761
|
title: "Get Release",
|
|
826
762
|
summary: "Get more details for a specific release by its ID, including source control information and associated builds",
|
|
@@ -830,25 +766,7 @@ export class BugsnagClient {
|
|
|
830
766
|
"Analyze the stability data and targets for a release",
|
|
831
767
|
"See the builds that make up the release",
|
|
832
768
|
],
|
|
833
|
-
|
|
834
|
-
...(this.projectApiKey
|
|
835
|
-
? []
|
|
836
|
-
: [
|
|
837
|
-
{
|
|
838
|
-
name: "projectId",
|
|
839
|
-
type: z.string(),
|
|
840
|
-
description: "ID of the project containing the release",
|
|
841
|
-
required: true,
|
|
842
|
-
},
|
|
843
|
-
]),
|
|
844
|
-
{
|
|
845
|
-
name: "releaseId",
|
|
846
|
-
type: z.string(),
|
|
847
|
-
description: "ID of the release to retrieve",
|
|
848
|
-
required: true,
|
|
849
|
-
examples: ["5f8d0d55c9e77c0017a1b2c3"],
|
|
850
|
-
},
|
|
851
|
-
],
|
|
769
|
+
inputSchema: getReleaseInputSchema,
|
|
852
770
|
examples: [
|
|
853
771
|
{
|
|
854
772
|
description: "Get details for a specific release",
|
|
@@ -863,16 +781,15 @@ export class BugsnagClient {
|
|
|
863
781
|
idempotent: true,
|
|
864
782
|
outputDescription: "JSON object containing release details along with stability metrics such as user and session stability, and whether it meets project targets",
|
|
865
783
|
}, async (args, _extra) => {
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
const
|
|
869
|
-
const releaseResponse = await this.projectApi.getReleaseGroup(args.releaseId);
|
|
784
|
+
const params = getReleaseInputSchema.parse(args);
|
|
785
|
+
const project = await this.getInputProject(params.projectId);
|
|
786
|
+
const releaseResponse = await this.projectApi.getReleaseGroup(params.releaseId);
|
|
870
787
|
if (!releaseResponse.body)
|
|
871
|
-
throw new ToolError(`No release for ${
|
|
788
|
+
throw new ToolError(`No release for ${params.releaseId} found.`);
|
|
872
789
|
const release = this.addStabilityData(releaseResponse.body, project);
|
|
873
790
|
let builds = [];
|
|
874
791
|
if (releaseResponse.body) {
|
|
875
|
-
const buildsResponse = await this.projectApi.listBuildsInRelease(
|
|
792
|
+
const buildsResponse = await this.projectApi.listBuildsInRelease(params.releaseId);
|
|
876
793
|
if (buildsResponse.body) {
|
|
877
794
|
builds = buildsResponse.body.map((b) => this.addStabilityData(b, project));
|
|
878
795
|
}
|
|
@@ -889,6 +806,10 @@ export class BugsnagClient {
|
|
|
889
806
|
],
|
|
890
807
|
};
|
|
891
808
|
});
|
|
809
|
+
const getBuildInputSchema = z.object({
|
|
810
|
+
projectId: toolInputParameters.projectId,
|
|
811
|
+
buildId: toolInputParameters.buildId,
|
|
812
|
+
});
|
|
892
813
|
register({
|
|
893
814
|
title: "Get Build",
|
|
894
815
|
summary: "Get more details for a specific build by its ID",
|
|
@@ -898,25 +819,7 @@ export class BugsnagClient {
|
|
|
898
819
|
"Analyze a specific build to correlate with error spikes or deployments",
|
|
899
820
|
"See the stability targets for a project and if the build meets them",
|
|
900
821
|
],
|
|
901
|
-
|
|
902
|
-
...(this.projectApiKey
|
|
903
|
-
? []
|
|
904
|
-
: [
|
|
905
|
-
{
|
|
906
|
-
name: "projectId",
|
|
907
|
-
type: z.string(),
|
|
908
|
-
description: "ID of the project containing the build",
|
|
909
|
-
required: true,
|
|
910
|
-
},
|
|
911
|
-
]),
|
|
912
|
-
{
|
|
913
|
-
name: "buildId",
|
|
914
|
-
type: z.string(),
|
|
915
|
-
description: "ID of the build to retrieve",
|
|
916
|
-
required: true,
|
|
917
|
-
examples: ["5f8d0d55c9e77c0017a1b2c3"],
|
|
918
|
-
},
|
|
919
|
-
],
|
|
822
|
+
inputSchema: getBuildInputSchema,
|
|
920
823
|
examples: [
|
|
921
824
|
{
|
|
922
825
|
description: "Get details for a specific build",
|
|
@@ -931,17 +834,475 @@ export class BugsnagClient {
|
|
|
931
834
|
idempotent: true,
|
|
932
835
|
outputDescription: "JSON object containing build details along with stability metrics such as user and session stability, and whether it meets project targets",
|
|
933
836
|
}, async (args, _extra) => {
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
const
|
|
937
|
-
const response = await this.projectApi.getProjectReleaseById(project.id, args.buildId);
|
|
837
|
+
const params = getBuildInputSchema.parse(args);
|
|
838
|
+
const project = await this.getInputProject(params.projectId);
|
|
839
|
+
const response = await this.projectApi.getProjectReleaseById(project.id, params.buildId);
|
|
938
840
|
if (!response.body)
|
|
939
|
-
throw new ToolError(`No build for ${
|
|
841
|
+
throw new ToolError(`No build for ${params.buildId} found.`);
|
|
940
842
|
const build = this.addStabilityData(response.body, project);
|
|
941
843
|
return {
|
|
942
844
|
content: [{ type: "text", text: JSON.stringify(build) }],
|
|
943
845
|
};
|
|
944
846
|
});
|
|
847
|
+
// ============================================================
|
|
848
|
+
// Performance Monitoring Tools
|
|
849
|
+
// ============================================================
|
|
850
|
+
const listSpanGroupsInputSchema = z.object({
|
|
851
|
+
projectId: toolInputParameters.projectId,
|
|
852
|
+
sort: z
|
|
853
|
+
.enum([
|
|
854
|
+
"total_spans",
|
|
855
|
+
"last_seen",
|
|
856
|
+
"name",
|
|
857
|
+
"display_name",
|
|
858
|
+
"network_http_method",
|
|
859
|
+
"rendering_slow_frame_span_percentage",
|
|
860
|
+
"rendering_frozen_frame_span_percentage",
|
|
861
|
+
"duration_p50",
|
|
862
|
+
"duration_p75",
|
|
863
|
+
"duration_p90",
|
|
864
|
+
"duration_p95",
|
|
865
|
+
"duration_p99",
|
|
866
|
+
"system_metrics_cpu_total_mean_p50",
|
|
867
|
+
"system_metrics_cpu_total_mean_p75",
|
|
868
|
+
"system_metrics_cpu_total_mean_p90",
|
|
869
|
+
"system_metrics_cpu_total_mean_p95",
|
|
870
|
+
"system_metrics_cpu_total_mean_p99",
|
|
871
|
+
"system_metrics_memory_device_mean_p50",
|
|
872
|
+
"system_metrics_memory_device_mean_p75",
|
|
873
|
+
"system_metrics_memory_device_mean_p90",
|
|
874
|
+
"system_metrics_memory_device_mean_p95",
|
|
875
|
+
"system_metrics_memory_device_mean_p99",
|
|
876
|
+
"rendering_metrics_fps_mean_p50",
|
|
877
|
+
"rendering_metrics_fps_mean_p75",
|
|
878
|
+
"rendering_metrics_fps_mean_p90",
|
|
879
|
+
"rendering_metrics_fps_mean_p95",
|
|
880
|
+
"rendering_metrics_fps_mean_p99",
|
|
881
|
+
"http_response_4xx_percentage",
|
|
882
|
+
"http_response_5xx_percentage",
|
|
883
|
+
])
|
|
884
|
+
.optional()
|
|
885
|
+
.describe("Field to sort by"),
|
|
886
|
+
direction: toolInputParameters.direction,
|
|
887
|
+
perPage: toolInputParameters.perPage,
|
|
888
|
+
starredOnly: z
|
|
889
|
+
.boolean()
|
|
890
|
+
.optional()
|
|
891
|
+
.describe("Show only starred span groups"),
|
|
892
|
+
nextUrl: toolInputParameters.nextUrl,
|
|
893
|
+
filters: toolInputParameters.performanceFilters,
|
|
894
|
+
});
|
|
895
|
+
register({
|
|
896
|
+
title: "List Span Groups",
|
|
897
|
+
summary: "List span groups (operations) tracked for performance monitoring",
|
|
898
|
+
purpose: "Discover and analyze different operations being monitored",
|
|
899
|
+
useCases: [
|
|
900
|
+
"View all operations being tracked for performance",
|
|
901
|
+
"Find slow operations by sorting by duration metrics",
|
|
902
|
+
"Filter to starred/important span groups",
|
|
903
|
+
],
|
|
904
|
+
inputSchema: listSpanGroupsInputSchema,
|
|
905
|
+
examples: [
|
|
906
|
+
{
|
|
907
|
+
description: "List slowest operations",
|
|
908
|
+
parameters: {
|
|
909
|
+
sort: "duration_p95",
|
|
910
|
+
direction: "desc",
|
|
911
|
+
perPage: 10,
|
|
912
|
+
},
|
|
913
|
+
expectedOutput: "Array of span groups sorted by 95th percentile duration",
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
description: "List starred span groups with filtering",
|
|
917
|
+
parameters: {
|
|
918
|
+
starredOnly: true,
|
|
919
|
+
filters: {
|
|
920
|
+
"span_group.category": [
|
|
921
|
+
{ type: "eq", value: "full_page_load" },
|
|
922
|
+
],
|
|
923
|
+
},
|
|
924
|
+
},
|
|
925
|
+
expectedOutput: "Array of starred span groups filtered by category",
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
hints: [
|
|
929
|
+
"Span groups represent different operation types (page loads, API calls, etc.)",
|
|
930
|
+
"Use sort by duration_p95 or duration_p99 to find the slowest operations",
|
|
931
|
+
"Star important span groups for quick access",
|
|
932
|
+
"Use nextUrl for pagination",
|
|
933
|
+
],
|
|
934
|
+
}, async (args, _extra) => {
|
|
935
|
+
const params = listSpanGroupsInputSchema.parse(args);
|
|
936
|
+
const project = await this.getInputProject(params.projectId);
|
|
937
|
+
const result = await this.projectApi.listProjectSpanGroups(project.id, params.sort, params.direction, params.perPage, undefined, params.filters, params.starredOnly, params.nextUrl);
|
|
938
|
+
return {
|
|
939
|
+
content: [
|
|
940
|
+
{
|
|
941
|
+
type: "text",
|
|
942
|
+
text: JSON.stringify({
|
|
943
|
+
data: result.body,
|
|
944
|
+
next_url: result.nextUrl,
|
|
945
|
+
count: result.body?.length,
|
|
946
|
+
}),
|
|
947
|
+
},
|
|
948
|
+
],
|
|
949
|
+
};
|
|
950
|
+
});
|
|
951
|
+
const getSpanGroupInputSchema = z.object({
|
|
952
|
+
projectId: toolInputParameters.projectId,
|
|
953
|
+
spanGroupId: toolInputParameters.spanGroupId,
|
|
954
|
+
filters: toolInputParameters.performanceFilters,
|
|
955
|
+
});
|
|
956
|
+
register({
|
|
957
|
+
title: "Get Span Group",
|
|
958
|
+
summary: "Get detailed performance metrics for a specific span group",
|
|
959
|
+
purpose: "Analyze performance characteristics of a specific operation",
|
|
960
|
+
useCases: [
|
|
961
|
+
"View detailed statistics (p50, p75, p90, p95, p99) for an operation",
|
|
962
|
+
"Check if performance targets are configured",
|
|
963
|
+
"Monitor span count to understand operation volume",
|
|
964
|
+
],
|
|
965
|
+
inputSchema: getSpanGroupInputSchema,
|
|
966
|
+
examples: [
|
|
967
|
+
{
|
|
968
|
+
description: "Get details for an API endpoint span group",
|
|
969
|
+
parameters: { spanGroupId: "[HttpClient]GET-api.example.com" },
|
|
970
|
+
expectedOutput: "Statistics, category, and performance target info",
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
description: "Get span group details with device filtering",
|
|
974
|
+
parameters: {
|
|
975
|
+
spanGroupId: "[HttpClient]GET-api.example.com",
|
|
976
|
+
filters: {
|
|
977
|
+
"device.browser_name": [{ type: "eq", value: "Chrome" }],
|
|
978
|
+
},
|
|
979
|
+
},
|
|
980
|
+
expectedOutput: "Statistics filtered for Chrome browser only",
|
|
981
|
+
},
|
|
982
|
+
],
|
|
983
|
+
hints: [
|
|
984
|
+
"Use List Span Groups first to discover available span group IDs",
|
|
985
|
+
"IDs are automatically URL-encoded - provide the raw ID",
|
|
986
|
+
"Statistics include p50, p75, p90, p95, p99 percentiles",
|
|
987
|
+
],
|
|
988
|
+
}, async (args, _extra) => {
|
|
989
|
+
const params = getSpanGroupInputSchema.parse(args);
|
|
990
|
+
const project = await this.getInputProject(params.projectId);
|
|
991
|
+
const spanGroupResults = await this.projectApi.getProjectSpanGroup(project.id, params.spanGroupId, params.filters);
|
|
992
|
+
const spanGroupTimelineResult = await this.projectApi.getProjectSpanGroupTimeline(project.id, params.spanGroupId, params.filters);
|
|
993
|
+
const spanGroupDistributionResult = await this.projectApi.getProjectSpanGroupDistribution(project.id, params.spanGroupId, params.filters);
|
|
994
|
+
const result = {
|
|
995
|
+
...spanGroupResults.body,
|
|
996
|
+
timeline: spanGroupTimelineResult.body,
|
|
997
|
+
distribution: spanGroupDistributionResult.body,
|
|
998
|
+
};
|
|
999
|
+
return {
|
|
1000
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
1001
|
+
};
|
|
1002
|
+
});
|
|
1003
|
+
const listSpansInputSchema = z.object({
|
|
1004
|
+
projectId: toolInputParameters.projectId,
|
|
1005
|
+
spanGroupId: toolInputParameters.spanGroupId,
|
|
1006
|
+
sort: z
|
|
1007
|
+
.enum([
|
|
1008
|
+
"duration",
|
|
1009
|
+
"timestamp",
|
|
1010
|
+
"full_page_load_lcp",
|
|
1011
|
+
"full_page_load_fid",
|
|
1012
|
+
"full_page_load_cls",
|
|
1013
|
+
"full_page_load_ttfb",
|
|
1014
|
+
"full_page_load_fcp",
|
|
1015
|
+
"rendering_slow_frame_percentage",
|
|
1016
|
+
"rendering_frozen_frame_percentage",
|
|
1017
|
+
"system_metrics_cpu_total_mean",
|
|
1018
|
+
"system_metrics_memory_device_mean",
|
|
1019
|
+
"rendering_metrics_fps_mean",
|
|
1020
|
+
"rendering_metrics_fps_minimum",
|
|
1021
|
+
"rendering_metrics_fps_maximum",
|
|
1022
|
+
"http_response_code",
|
|
1023
|
+
])
|
|
1024
|
+
.optional()
|
|
1025
|
+
.describe("Field to sort by"),
|
|
1026
|
+
direction: toolInputParameters.direction,
|
|
1027
|
+
perPage: toolInputParameters.perPage,
|
|
1028
|
+
nextUrl: toolInputParameters.nextUrl,
|
|
1029
|
+
filters: toolInputParameters.performanceFilters,
|
|
1030
|
+
});
|
|
1031
|
+
register({
|
|
1032
|
+
title: "List Spans",
|
|
1033
|
+
summary: "Get individual spans belonging to a span group",
|
|
1034
|
+
purpose: "Examine individual operation instances within a span group",
|
|
1035
|
+
useCases: [
|
|
1036
|
+
"Analyze individual slow operations",
|
|
1037
|
+
"Debug performance issues by examining specific traces",
|
|
1038
|
+
"Find patterns in operation attributes",
|
|
1039
|
+
],
|
|
1040
|
+
inputSchema: listSpansInputSchema,
|
|
1041
|
+
examples: [
|
|
1042
|
+
{
|
|
1043
|
+
description: "Get slowest spans for an operation",
|
|
1044
|
+
parameters: {
|
|
1045
|
+
spanGroupId: "[HttpClient]GET-api.example.com",
|
|
1046
|
+
sort: "duration",
|
|
1047
|
+
direction: "desc",
|
|
1048
|
+
perPage: 10,
|
|
1049
|
+
},
|
|
1050
|
+
expectedOutput: "Array of the 10 slowest span instances",
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
description: "Get spans filtered by OS with pagination",
|
|
1054
|
+
parameters: {
|
|
1055
|
+
spanGroupId: "[HttpClient]GET-api.example.com",
|
|
1056
|
+
sort: "timestamp",
|
|
1057
|
+
filters: {
|
|
1058
|
+
"os.name": [{ type: "eq", value: "iOS" }],
|
|
1059
|
+
},
|
|
1060
|
+
nextUrl: "/projects/123/spans?offset=30&per_page=30",
|
|
1061
|
+
},
|
|
1062
|
+
expectedOutput: "Array of spans from iOS devices with next page navigation",
|
|
1063
|
+
},
|
|
1064
|
+
],
|
|
1065
|
+
hints: [
|
|
1066
|
+
"Sort by duration descending to find the slowest instances",
|
|
1067
|
+
"Each span includes trace ID for further investigation",
|
|
1068
|
+
],
|
|
1069
|
+
}, async (args, _extra) => {
|
|
1070
|
+
const params = listSpansInputSchema.parse(args);
|
|
1071
|
+
const project = await this.getInputProject(params.projectId);
|
|
1072
|
+
const result = await this.projectApi.listSpansBySpanGroupId(project.id, params.spanGroupId, params.filters, params.sort, params.direction, params.perPage, params.nextUrl);
|
|
1073
|
+
return {
|
|
1074
|
+
content: [
|
|
1075
|
+
{
|
|
1076
|
+
type: "text",
|
|
1077
|
+
text: JSON.stringify({
|
|
1078
|
+
data: result.body,
|
|
1079
|
+
next_url: result.nextUrl,
|
|
1080
|
+
count: result.body?.length,
|
|
1081
|
+
}),
|
|
1082
|
+
},
|
|
1083
|
+
],
|
|
1084
|
+
};
|
|
1085
|
+
});
|
|
1086
|
+
const getTraceInputSchema = z.object({
|
|
1087
|
+
projectId: toolInputParameters.projectId,
|
|
1088
|
+
traceId: z.string().describe("Trace ID"),
|
|
1089
|
+
from: z.string().describe("Start time (ISO 8601 format)"),
|
|
1090
|
+
to: z.string().describe("End time (ISO 8601 format)"),
|
|
1091
|
+
targetSpanId: z
|
|
1092
|
+
.string()
|
|
1093
|
+
.optional()
|
|
1094
|
+
.describe("Optional target span ID to focus on"),
|
|
1095
|
+
perPage: toolInputParameters.perPage,
|
|
1096
|
+
nextUrl: toolInputParameters.nextUrl,
|
|
1097
|
+
});
|
|
1098
|
+
register({
|
|
1099
|
+
title: "Get Trace",
|
|
1100
|
+
summary: "Get all spans within a specific trace",
|
|
1101
|
+
purpose: "View the complete trace of operations for a request/transaction",
|
|
1102
|
+
useCases: [
|
|
1103
|
+
"Debug slow requests by viewing all operations in the trace",
|
|
1104
|
+
"Understand the flow of a request through the system",
|
|
1105
|
+
"Identify bottlenecks in distributed systems",
|
|
1106
|
+
],
|
|
1107
|
+
inputSchema: getTraceInputSchema,
|
|
1108
|
+
examples: [
|
|
1109
|
+
{
|
|
1110
|
+
description: "Get all spans for a trace",
|
|
1111
|
+
parameters: {
|
|
1112
|
+
traceId: "abc123",
|
|
1113
|
+
from: "2024-01-01T00:00:00Z",
|
|
1114
|
+
to: "2024-01-01T23:59:59Z",
|
|
1115
|
+
},
|
|
1116
|
+
expectedOutput: "Array of all spans in the trace with timing and hierarchy",
|
|
1117
|
+
},
|
|
1118
|
+
{
|
|
1119
|
+
description: "Get spans for a trace with pagination and target span",
|
|
1120
|
+
parameters: {
|
|
1121
|
+
traceId: "def456",
|
|
1122
|
+
from: "2024-01-01T00:00:00Z",
|
|
1123
|
+
to: "2024-01-01T23:59:59Z",
|
|
1124
|
+
targetSpanId: "span-789",
|
|
1125
|
+
perPage: 50,
|
|
1126
|
+
},
|
|
1127
|
+
expectedOutput: "Array of up to 50 spans focused around the target span",
|
|
1128
|
+
},
|
|
1129
|
+
],
|
|
1130
|
+
hints: [
|
|
1131
|
+
"Traces show the complete execution path of a request",
|
|
1132
|
+
"Use from/to parameters to narrow the time window",
|
|
1133
|
+
"targetSpanId can be used to focus on a specific span in the trace",
|
|
1134
|
+
],
|
|
1135
|
+
}, async (args, _extra) => {
|
|
1136
|
+
const params = getTraceInputSchema.parse(args);
|
|
1137
|
+
const project = await this.getInputProject(params.projectId);
|
|
1138
|
+
if (!params.traceId || !params.from || !params.to) {
|
|
1139
|
+
throw new ToolError("traceId, from, and to are required");
|
|
1140
|
+
}
|
|
1141
|
+
const result = await this.projectApi.listSpansByTraceId(project.id, params.traceId, params.from, params.to, params.targetSpanId, params.perPage, params.nextUrl);
|
|
1142
|
+
return {
|
|
1143
|
+
content: [
|
|
1144
|
+
{
|
|
1145
|
+
type: "text",
|
|
1146
|
+
text: JSON.stringify({
|
|
1147
|
+
data: result.body,
|
|
1148
|
+
next_url: result.nextUrl,
|
|
1149
|
+
count: result.body?.length,
|
|
1150
|
+
}),
|
|
1151
|
+
},
|
|
1152
|
+
],
|
|
1153
|
+
};
|
|
1154
|
+
});
|
|
1155
|
+
const listTraceFieldsInputSchema = z.object({
|
|
1156
|
+
projectId: toolInputParameters.projectId,
|
|
1157
|
+
});
|
|
1158
|
+
// Similar to event filters, consider caching
|
|
1159
|
+
register({
|
|
1160
|
+
title: "List Trace Fields",
|
|
1161
|
+
summary: "Get available trace fields/attributes for filtering",
|
|
1162
|
+
purpose: "Discover what custom attributes are available for filtering",
|
|
1163
|
+
useCases: [
|
|
1164
|
+
"Find available custom attributes for performance filtering",
|
|
1165
|
+
"Understand what metadata is attached to traces",
|
|
1166
|
+
"Build dynamic filters based on available fields",
|
|
1167
|
+
],
|
|
1168
|
+
inputSchema: listTraceFieldsInputSchema,
|
|
1169
|
+
examples: [
|
|
1170
|
+
{
|
|
1171
|
+
description: "Get all trace fields",
|
|
1172
|
+
parameters: {},
|
|
1173
|
+
expectedOutput: "Array of field names and types available for filtering",
|
|
1174
|
+
},
|
|
1175
|
+
],
|
|
1176
|
+
hints: [
|
|
1177
|
+
"Trace fields are custom attributes added to spans",
|
|
1178
|
+
"Use these fields for filtering other performance queries",
|
|
1179
|
+
],
|
|
1180
|
+
}, async (args, _extra) => {
|
|
1181
|
+
const params = listTraceFieldsInputSchema.parse(args);
|
|
1182
|
+
const project = await this.getInputProject(params.projectId);
|
|
1183
|
+
const traceFields = await this.getProjectTraceFields(project);
|
|
1184
|
+
return {
|
|
1185
|
+
content: [{ type: "text", text: JSON.stringify(traceFields) }],
|
|
1186
|
+
};
|
|
1187
|
+
});
|
|
1188
|
+
const getNetworkGroupingInputSchema = z.object({
|
|
1189
|
+
projectId: toolInputParameters.projectId,
|
|
1190
|
+
});
|
|
1191
|
+
register({
|
|
1192
|
+
title: "Get Network Endpoint Groupings",
|
|
1193
|
+
summary: "Get the network endpoint grouping rules for a project",
|
|
1194
|
+
purpose: "Retrieve the URL patterns used to group network spans for performance monitoring",
|
|
1195
|
+
useCases: [
|
|
1196
|
+
"View current network endpoint grouping configuration",
|
|
1197
|
+
"Understand how network requests are being grouped in performance monitoring",
|
|
1198
|
+
"Check grouping patterns before making updates",
|
|
1199
|
+
],
|
|
1200
|
+
inputSchema: getNetworkGroupingInputSchema,
|
|
1201
|
+
examples: [
|
|
1202
|
+
{
|
|
1203
|
+
description: "Get network grouping rules for a project",
|
|
1204
|
+
parameters: {},
|
|
1205
|
+
expectedOutput: "Array of endpoint URL patterns",
|
|
1206
|
+
},
|
|
1207
|
+
],
|
|
1208
|
+
hints: [
|
|
1209
|
+
"Network grouping patterns help consolidate similar requests into single span groups",
|
|
1210
|
+
"Patterns use OpenAPI path templating syntax with curly braces for path parameters (e.g., /users/{userId})",
|
|
1211
|
+
"Wildcards (*) can be used in domains to match multiple subdomains (e.g., https://*.example.com)",
|
|
1212
|
+
],
|
|
1213
|
+
readOnly: true,
|
|
1214
|
+
idempotent: true,
|
|
1215
|
+
}, async (args, _extra) => {
|
|
1216
|
+
const params = getNetworkGroupingInputSchema.parse(args);
|
|
1217
|
+
const project = await this.getInputProject(params.projectId);
|
|
1218
|
+
const result = await this.projectApi.getProjectNetworkGroupingRuleset(project.id);
|
|
1219
|
+
return {
|
|
1220
|
+
content: [
|
|
1221
|
+
{ type: "text", text: JSON.stringify(result.body.endpoints || []) },
|
|
1222
|
+
],
|
|
1223
|
+
};
|
|
1224
|
+
});
|
|
1225
|
+
const setNetworkGroupingInputSchema = z.object({
|
|
1226
|
+
projectId: toolInputParameters.projectId,
|
|
1227
|
+
endpoints: z
|
|
1228
|
+
.array(z.string())
|
|
1229
|
+
.describe("Array of URL patterns by which network spans are grouped. " +
|
|
1230
|
+
"Endpoints follow OpenAPI path templating syntax (https://swagger.io/specification/#path-templating) where path parameters use curly braces (e.g., /users/{id}). " +
|
|
1231
|
+
"If you encounter colon-prefixed parameters (e.g., :userId from Express/React Router), convert them to curly braces (e.g., {userId}). " +
|
|
1232
|
+
"Wildcards (*) can be used in domains (e.g., https://*.example.com) to match multiple subdomains."),
|
|
1233
|
+
});
|
|
1234
|
+
register({
|
|
1235
|
+
title: "Set Network Endpoint Groupings",
|
|
1236
|
+
summary: "Set the network endpoint grouping rules for a project",
|
|
1237
|
+
purpose: "Configure URL patterns to control how network spans are grouped in performance monitoring",
|
|
1238
|
+
useCases: [
|
|
1239
|
+
"Consolidate similar API endpoints into single span groups",
|
|
1240
|
+
"Group dynamic URLs using path parameters (e.g., /api/users/{userId} groups /api/users/123, /api/users/456)",
|
|
1241
|
+
"Match multiple subdomains using wildcards (e.g., https://*.example.com groups api.example.com, cdn.example.com)",
|
|
1242
|
+
"Simplify performance monitoring by reducing span group clutter",
|
|
1243
|
+
],
|
|
1244
|
+
inputSchema: setNetworkGroupingInputSchema,
|
|
1245
|
+
examples: [
|
|
1246
|
+
{
|
|
1247
|
+
description: "Group API endpoints with path parameters",
|
|
1248
|
+
parameters: {
|
|
1249
|
+
endpoints: [
|
|
1250
|
+
"/api/users/{userId}",
|
|
1251
|
+
"/api/products/{productId}",
|
|
1252
|
+
"/api/orders/{orderId}/items/{itemId}",
|
|
1253
|
+
],
|
|
1254
|
+
},
|
|
1255
|
+
expectedOutput: "Success response confirming the update",
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
description: "Group endpoints with domain wildcards and path parameters",
|
|
1259
|
+
parameters: {
|
|
1260
|
+
endpoints: [
|
|
1261
|
+
"https://*.example.com/api/v1/{resourceId}",
|
|
1262
|
+
"https://api.example.com/v2/users/{userId}",
|
|
1263
|
+
"/graphql",
|
|
1264
|
+
],
|
|
1265
|
+
},
|
|
1266
|
+
expectedOutput: "Success response confirming the update",
|
|
1267
|
+
},
|
|
1268
|
+
{
|
|
1269
|
+
description: "Convert colon-prefixed parameters to curly braces (e.g., from Express/React Router)",
|
|
1270
|
+
parameters: {
|
|
1271
|
+
endpoints: [
|
|
1272
|
+
"/{organizationSlug}/{projectSlug}/performance/view-load",
|
|
1273
|
+
"/api/{version}/items/{itemId}",
|
|
1274
|
+
],
|
|
1275
|
+
},
|
|
1276
|
+
expectedOutput: "Success response confirming the update",
|
|
1277
|
+
},
|
|
1278
|
+
],
|
|
1279
|
+
hints: [
|
|
1280
|
+
"Use Get Network Grouping first to see current patterns",
|
|
1281
|
+
"Use OpenAPI path templating with curly braces for path parameters: /users/{userId}, /orders/{orderId}/items/{itemId}",
|
|
1282
|
+
"Convert colon-prefixed parameters to curly braces: :organizationSlug becomes {organizationSlug}, :projectSlug becomes {projectSlug}",
|
|
1283
|
+
"Wildcards (*) can be used in domains to match subdomains: https://*.example.com/api",
|
|
1284
|
+
"This replaces all existing patterns - include all patterns you want to keep",
|
|
1285
|
+
"Well-designed patterns reduce noise in performance monitoring",
|
|
1286
|
+
],
|
|
1287
|
+
readOnly: false,
|
|
1288
|
+
idempotent: true,
|
|
1289
|
+
}, async (args, _extra) => {
|
|
1290
|
+
const params = setNetworkGroupingInputSchema.parse(args);
|
|
1291
|
+
const project = await this.getInputProject(params.projectId);
|
|
1292
|
+
const result = await this.projectApi.updateProjectNetworkGroupingRuleset(project.id, params.endpoints);
|
|
1293
|
+
return {
|
|
1294
|
+
content: [
|
|
1295
|
+
{
|
|
1296
|
+
type: "text",
|
|
1297
|
+
text: JSON.stringify({
|
|
1298
|
+
success: result.status === 200 || result.status === 204,
|
|
1299
|
+
projectId: project.id,
|
|
1300
|
+
endpoints: params.endpoints,
|
|
1301
|
+
}),
|
|
1302
|
+
},
|
|
1303
|
+
],
|
|
1304
|
+
};
|
|
1305
|
+
});
|
|
945
1306
|
}
|
|
946
1307
|
registerResources(register) {
|
|
947
1308
|
register("event", "{id}", async (uri, variables, _extra) => {
|