@smartbear/mcp 0.6.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.
Files changed (35) hide show
  1. package/README.md +20 -3
  2. package/dist/api-hub/client/api.js +253 -0
  3. package/dist/api-hub/client/configuration.js +27 -0
  4. package/dist/api-hub/client/index.js +5 -0
  5. package/dist/api-hub/client/portal-types.js +131 -0
  6. package/dist/api-hub/client/registry-types.js +55 -0
  7. package/dist/api-hub/client/tools.js +86 -0
  8. package/dist/api-hub/client.js +64 -404
  9. package/dist/bugsnag/client/api/CurrentUser.js +16 -10
  10. package/dist/bugsnag/client/api/Error.js +35 -35
  11. package/dist/bugsnag/client/api/Project.js +21 -9
  12. package/dist/bugsnag/client/api/base.js +7 -4
  13. package/dist/bugsnag/client/api/filters.js +9 -9
  14. package/dist/bugsnag/client.js +165 -140
  15. package/dist/common/info.js +1 -1
  16. package/dist/common/server.js +35 -27
  17. package/dist/index.js +11 -4
  18. package/dist/pactflow/client/ai.js +20 -20
  19. package/dist/pactflow/client/base.js +48 -13
  20. package/dist/pactflow/client/prompts.js +10 -12
  21. package/dist/pactflow/client/tools.js +10 -10
  22. package/dist/pactflow/client/utils.js +1 -1
  23. package/dist/pactflow/client.js +16 -9
  24. package/dist/qmetry/client/api/client-api.js +39 -0
  25. package/dist/qmetry/client/handlers.js +11 -0
  26. package/dist/qmetry/client/project.js +27 -0
  27. package/dist/qmetry/client/testcase.js +104 -0
  28. package/dist/qmetry/client/tools.js +222 -0
  29. package/dist/qmetry/client.js +95 -0
  30. package/dist/qmetry/config/constants.js +12 -0
  31. package/dist/qmetry/config/rest-endpoints.js +11 -0
  32. package/dist/qmetry/types/common.js +174 -0
  33. package/dist/qmetry/types/testcase.js +19 -0
  34. package/dist/reflect/client.js +14 -14
  35. package/package.json +6 -5
@@ -1,10 +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 { CurrentUserAPI, ErrorAPI, Configuration } from "./client/index.js";
5
- import { FilterObjectSchema, toQueryString } from "./client/api/filters.js";
6
- import { ProjectAPI, } from "./client/api/Project.js";
7
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";
8
8
  const HUB_PREFIX = "00000";
9
9
  const DEFAULT_DOMAIN = "bugsnag.com";
10
10
  const HUB_DOMAIN = "bugsnag.smartbear.com";
@@ -15,11 +15,11 @@ const cacheKeys = {
15
15
  CURRENT_PROJECT_EVENT_FILTERS: "bugsnag_current_project_event_filters",
16
16
  BUILD: "bugsnag_build", // + buildId
17
17
  RELEASE: "bugsnag_release", // + releaseId
18
- BUILDS_IN_RELEASE: "bugsnag_builds_in_release" // + releaseId
18
+ BUILDS_IN_RELEASE: "bugsnag_builds_in_release", // + releaseId
19
19
  };
20
20
  // Exclude certain event fields from the project event filters to improve agent usage
21
21
  const EXCLUDED_EVENT_FIELDS = new Set([
22
- "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
23
23
  ]);
24
24
  const PERMITTED_UPDATE_OPERATIONS = [
25
25
  "override_severity",
@@ -27,7 +27,7 @@ const PERMITTED_UPDATE_OPERATIONS = [
27
27
  "fix",
28
28
  "ignore",
29
29
  "discard",
30
- "undiscard"
30
+ "undiscard",
31
31
  ];
32
32
  export class BugsnagClient {
33
33
  currentUserApi;
@@ -82,7 +82,7 @@ export class BugsnagClient {
82
82
  }
83
83
  }
84
84
  getHost(apiKey, subdomain) {
85
- if (apiKey && apiKey.startsWith(HUB_PREFIX)) {
85
+ if (apiKey?.startsWith(HUB_PREFIX)) {
86
86
  return `https://${subdomain}.${HUB_DOMAIN}`;
87
87
  }
88
88
  else {
@@ -95,7 +95,7 @@ export class BugsnagClient {
95
95
  getEndpoint(subdomain, apiKey, endpoint) {
96
96
  let subDomainEndpoint;
97
97
  if (!endpoint) {
98
- if (apiKey && apiKey.startsWith(HUB_PREFIX)) {
98
+ if (apiKey?.startsWith(HUB_PREFIX)) {
99
99
  subDomainEndpoint = `https://${subdomain}.${HUB_DOMAIN}`;
100
100
  }
101
101
  else {
@@ -105,7 +105,8 @@ export class BugsnagClient {
105
105
  else {
106
106
  // check if the endpoint matches either the HUB_DOMAIN or DEFAULT_DOMAIN
107
107
  const url = new URL(endpoint);
108
- if (url.hostname.endsWith(HUB_DOMAIN) || url.hostname.endsWith(DEFAULT_DOMAIN)) {
108
+ if (url.hostname.endsWith(HUB_DOMAIN) ||
109
+ url.hostname.endsWith(DEFAULT_DOMAIN)) {
109
110
  // For known domains (Hub or Bugsnag), always use HTTPS and standard format
110
111
  if (url.hostname.endsWith(HUB_DOMAIN)) {
111
112
  subDomainEndpoint = `https://${subdomain}.${HUB_DOMAIN}`;
@@ -124,7 +125,7 @@ export class BugsnagClient {
124
125
  async getDashboardUrl(project) {
125
126
  return `${this.appEndpoint}/${(await this.getOrganization()).slug}/${project.slug}`;
126
127
  }
127
- async getErrorUrl(project, errorId, queryString = '') {
128
+ async getErrorUrl(project, errorId, queryString = "") {
128
129
  const dashboardUrl = await this.getDashboardUrl(project);
129
130
  return `${dashboardUrl}/errors/${errorId}${queryString}`;
130
131
  }
@@ -165,7 +166,7 @@ export class BugsnagClient {
165
166
  const projects = await this.getProjects();
166
167
  project = projects.find((p) => p.api_key === this.projectApiKey) ?? null;
167
168
  if (!project) {
168
- throw new Error(`Unable to find project with API key ${this.projectApiKey} in organization.`);
169
+ throw new Error("Unable to find project with the configured API key.");
169
170
  }
170
171
  this.cache.set(cacheKeys.CURRENT_PROJECT, project);
171
172
  if (project) {
@@ -179,24 +180,26 @@ export class BugsnagClient {
179
180
  if (!filtersResponse || filtersResponse.length === 0) {
180
181
  throw new Error(`No event fields found for project ${project.name}.`);
181
182
  }
182
- filtersResponse = filtersResponse.filter(field => !EXCLUDED_EVENT_FIELDS.has(field.display_id));
183
+ filtersResponse = filtersResponse.filter((field) => !EXCLUDED_EVENT_FIELDS.has(field.display_id));
183
184
  return filtersResponse;
184
185
  }
185
186
  async getEvent(eventId, projectId) {
186
- const projectIds = projectId ? [projectId] : (await this.getProjects()).map((p) => p.id);
187
- const projectEvents = await Promise.all(projectIds.map((projectId) => this.errorsApi.viewEventById(projectId, eventId).catch(_e => null)));
188
- return projectEvents.find(event => event && !!event.body)?.body || null;
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;
189
192
  }
190
193
  async updateError(projectId, errorId, operation, options) {
191
194
  const errorUpdateRequest = {
192
195
  operation: operation,
193
- ...options
196
+ ...options,
194
197
  };
195
198
  const response = await this.errorsApi.updateErrorOnProject(projectId, errorId, errorUpdateRequest);
196
199
  return response.status === 200 || response.status === 204;
197
200
  }
198
201
  async getInputProject(projectId) {
199
- if (typeof projectId === 'string') {
202
+ if (typeof projectId === "string") {
200
203
  const maybeProject = await this.getProject(projectId);
201
204
  if (!maybeProject) {
202
205
  throw new Error(`Project with ID ${projectId} not found.`);
@@ -206,7 +209,7 @@ export class BugsnagClient {
206
209
  else {
207
210
  const currentProject = await this.getCurrentProject();
208
211
  if (!currentProject) {
209
- throw new Error('No current project found. Please provide a projectId or configure a project API key.');
212
+ throw new Error("No current project found. Please provide a projectId or configure a project API key.");
210
213
  }
211
214
  return currentProject;
212
215
  }
@@ -224,7 +227,8 @@ export class BugsnagClient {
224
227
  const build = this.cache.get(cacheKey);
225
228
  if (build)
226
229
  return build;
227
- const fetchedBuild = (await this.projectApi.getBuild(projectId, buildId)).body;
230
+ const fetchedBuild = (await this.projectApi.getBuild(projectId, buildId))
231
+ .body;
228
232
  if (!fetchedBuild)
229
233
  throw new Error(`No build for ${buildId} found.`);
230
234
  const stabilityTargets = await this.getProjectStabilityTargets(projectId);
@@ -269,11 +273,13 @@ export class BugsnagClient {
269
273
  const { stability_target_type, target_stability, critical_stability } = stabilityTargets;
270
274
  const user_stability = source.accumulative_daily_users_seen === 0 // avoid division by zero
271
275
  ? 0
272
- : (source.accumulative_daily_users_seen - source.accumulative_daily_users_with_unhandled) /
276
+ : (source.accumulative_daily_users_seen -
277
+ source.accumulative_daily_users_with_unhandled) /
273
278
  source.accumulative_daily_users_seen;
274
279
  const session_stability = source.total_sessions_count === 0 // avoid division by zero
275
280
  ? 0
276
- : (source.total_sessions_count - source.unhandled_sessions_count) / source.total_sessions_count;
281
+ : (source.total_sessions_count - source.unhandled_sessions_count) /
282
+ source.total_sessions_count;
277
283
  const stabilityMetric = stability_target_type === "user" ? user_stability : session_stability;
278
284
  const meets_target_stability = stabilityMetric >= target_stability.value;
279
285
  const meets_critical_stability = stabilityMetric >= critical_stability.value;
@@ -297,7 +303,7 @@ export class BugsnagClient {
297
303
  useCases: [
298
304
  "Browse available projects when no specific project API key is configured",
299
305
  "Find project IDs needed for other tools",
300
- "Get an overview of all projects in the organization"
306
+ "Get an overview of all projects in the organization",
301
307
  ],
302
308
  parameters: [
303
309
  {
@@ -305,35 +311,35 @@ export class BugsnagClient {
305
311
  type: z.number(),
306
312
  description: "Number of projects to return per page for pagination",
307
313
  required: false,
308
- examples: ["10", "25", "50"]
314
+ examples: ["10", "25", "50"],
309
315
  },
310
316
  {
311
317
  name: "page",
312
318
  type: z.number(),
313
319
  description: "Page number to return (starts from 1)",
314
320
  required: false,
315
- examples: ["1", "2", "3"]
316
- }
321
+ examples: ["1", "2", "3"],
322
+ },
317
323
  ],
318
324
  examples: [
319
325
  {
320
326
  description: "Get first 10 projects",
321
327
  parameters: {
322
328
  page_size: 10,
323
- page: 1
329
+ page: 1,
324
330
  },
325
331
  expectedOutput: "JSON array of project objects with IDs, names, and metadata",
326
332
  },
327
333
  {
328
334
  description: "Get all projects (no pagination)",
329
335
  parameters: {},
330
- expectedOutput: "JSON array of all available projects"
331
- }
336
+ expectedOutput: "JSON array of all available projects",
337
+ },
332
338
  ],
333
339
  hints: [
334
340
  "Use pagination for organizations with many projects to avoid large responses",
335
- "Project IDs from this list can be used with other tools when no project API key is configured"
336
- ]
341
+ "Project IDs from this list can be used with other tools when no project API key is configured",
342
+ ],
337
343
  }, async (args, _extra) => {
338
344
  let projects = await this.getProjects();
339
345
  if (!projects || projects.length === 0) {
@@ -363,7 +369,7 @@ export class BugsnagClient {
363
369
  "Investigate a specific error found through the List Project Errors tool",
364
370
  "Understand which types of user are affected by the error using summarized event data",
365
371
  "Get error details for debugging and root cause analysis",
366
- "Retrieve error metadata for incident reports and documentation"
372
+ "Retrieve error metadata for incident reports and documentation",
367
373
  ],
368
374
  parameters: [
369
375
  {
@@ -371,16 +377,18 @@ export class BugsnagClient {
371
377
  type: z.string(),
372
378
  required: true,
373
379
  description: "Unique identifier of the error to retrieve",
374
- examples: ["6863e2af8c857c0a5023b411"]
380
+ examples: ["6863e2af8c857c0a5023b411"],
375
381
  },
376
- ...(this.projectApiKey ? [] : [
377
- {
378
- name: "projectId",
379
- type: z.string(),
380
- required: true,
381
- description: "ID of the project containing the error",
382
- }
383
- ]),
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
+ ]),
384
392
  {
385
393
  name: "filters",
386
394
  type: FilterObjectSchema,
@@ -390,14 +398,14 @@ export class BugsnagClient {
390
398
  '{"error.status": [{"type": "eq", "value": "open"}]}',
391
399
  '{"event.since": [{"type": "eq", "value": "7d"}]} // Relative time: last 7 days',
392
400
  '{"event.since": [{"type": "eq", "value": "2018-05-20T00:00:00Z"}]} // ISO 8601 UTC format',
393
- '{"user.email": [{"type": "eq", "value": "user@example.com"}]}'
401
+ '{"user.email": [{"type": "eq", "value": "user@example.com"}]}',
394
402
  ],
395
403
  constraints: [
396
404
  "Time filters support ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h)",
397
405
  "ISO 8601 times must be in UTC and use extended format",
398
- "Relative time periods: h (hours), d (days)"
399
- ]
400
- }
406
+ "Relative time periods: h (hours), d (days)",
407
+ ],
408
+ },
401
409
  ],
402
410
  outputFormat: "JSON object containing: " +
403
411
  " - error_details: Aggregated data about the error, including first and last seen occurrence" +
@@ -408,10 +416,10 @@ export class BugsnagClient {
408
416
  {
409
417
  description: "Get details for a specific error",
410
418
  parameters: {
411
- errorId: "6863e2af8c857c0a5023b411"
419
+ errorId: "6863e2af8c857c0a5023b411",
412
420
  },
413
- expectedOutput: "JSON object with error details including message, stack trace, occurrence count, and metadata"
414
- }
421
+ expectedOutput: "JSON object with error details including message, stack trace, occurrence count, and metadata",
422
+ },
415
423
  ],
416
424
  hints: [
417
425
  "Error IDs can be found using the List Project Errors tool",
@@ -430,13 +438,13 @@ export class BugsnagClient {
430
438
  // Build query parameters
431
439
  const params = new URLSearchParams();
432
440
  // Add sorting and pagination parameters to get the latest event
433
- params.append('sort', 'timestamp');
434
- params.append('direction', 'desc');
435
- params.append('per_page', '1');
436
- params.append('full_reports', 'true');
441
+ params.append("sort", "timestamp");
442
+ params.append("direction", "desc");
443
+ params.append("per_page", "1");
444
+ params.append("full_reports", "true");
437
445
  const filters = {
438
- "error": [{ type: "eq", value: args.errorId }],
439
- ...args.filters
446
+ error: [{ type: "eq", value: args.errorId }],
447
+ ...args.filters,
440
448
  };
441
449
  const filtersQueryString = toQueryString(filters);
442
450
  const listEventsQueryString = `?${params}&${filtersQueryString}`;
@@ -454,11 +462,12 @@ export class BugsnagClient {
454
462
  const content = {
455
463
  error_details: errorDetails,
456
464
  latest_event: latestEvent,
457
- pivots: (await this.errorsApi.listErrorPivots(project.id, args.errorId)).body || [],
465
+ pivots: (await this.errorsApi.listErrorPivots(project.id, args.errorId))
466
+ .body || [],
458
467
  url: await this.getErrorUrl(project, args.errorId, `?${filtersQueryString}`),
459
468
  };
460
469
  return {
461
- content: [{ type: "text", text: JSON.stringify(content) }]
470
+ content: [{ type: "text", text: JSON.stringify(content) }],
462
471
  };
463
472
  });
464
473
  register({
@@ -468,7 +477,7 @@ export class BugsnagClient {
468
477
  useCases: [
469
478
  "Get event details when given a dashboard URL from a user or notification",
470
479
  "Extract event information from shared links or browser URLs",
471
- "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",
472
481
  ],
473
482
  parameters: [
474
483
  {
@@ -477,32 +486,32 @@ export class BugsnagClient {
477
486
  description: "Full URL to the event details page in the BugSnag dashboard (web interface)",
478
487
  required: true,
479
488
  examples: [
480
- "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",
481
490
  ],
482
491
  constraints: [
483
- "Must be a valid dashboard URL containing project slug and event_id parameter"
484
- ]
485
- }
492
+ "Must be a valid dashboard URL containing project slug and event_id parameter",
493
+ ],
494
+ },
486
495
  ],
487
496
  examples: [
488
497
  {
489
498
  description: "Get event details from a dashboard URL",
490
499
  parameters: {
491
- 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",
492
501
  },
493
- expectedOutput: "JSON object with complete event details including stack trace, metadata, and context"
494
- }
502
+ expectedOutput: "JSON object with complete event details including stack trace, metadata, and context",
503
+ },
495
504
  ],
496
505
  hints: [
497
506
  "The URL must contain both project slug in the path and event_id in query parameters",
498
- "This is useful when users share BugSnag dashboard URLs and you need to extract the event data"
499
- ]
507
+ "This is useful when users share BugSnag dashboard URLs and you need to extract the event data",
508
+ ],
500
509
  }, async (args, _extra) => {
501
510
  if (!args.link)
502
511
  throw new Error("link argument is required");
503
512
  const url = new URL(args.link);
504
513
  const eventId = url.searchParams.get("event_id");
505
- const projectSlug = url.pathname.split('/')[2];
514
+ const projectSlug = url.pathname.split("/")[2];
506
515
  if (!projectSlug || !eventId)
507
516
  throw new Error("Both projectSlug and eventId must be present in the link");
508
517
  // get the project id from list of projects
@@ -524,14 +533,14 @@ export class BugsnagClient {
524
533
  "Debug recent application errors by filtering for open errors in the last 7 days",
525
534
  "Generate error reports for stakeholders by filtering specific error types or severity levels",
526
535
  "Monitor error trends over time using date range filters",
527
- "Find errors affecting specific users or environments using metadata filters"
536
+ "Find errors affecting specific users or environments using metadata filters",
528
537
  ],
529
538
  parameters: [
530
539
  {
531
540
  name: "filters",
532
541
  type: FilterObjectSchema.default({
533
542
  "event.since": [{ type: "eq", value: "30d" }],
534
- "error.status": [{ type: "eq", value: "open" }]
543
+ "error.status": [{ type: "eq", value: "open" }],
535
544
  }),
536
545
  description: "Apply filters to narrow down the error list. Use the List Project Event Filters tool to discover available filter fields",
537
546
  required: false,
@@ -539,84 +548,92 @@ export class BugsnagClient {
539
548
  '{"error.status": [{"type": "eq", "value": "open"}]}',
540
549
  '{"event.since": [{"type": "eq", "value": "7d"}]} // Relative time: last 7 days',
541
550
  '{"event.since": [{"type": "eq", "value": "2018-05-20T00:00:00Z"}]} // ISO 8601 UTC format',
542
- '{"user.email": [{"type": "eq", "value": "user@example.com"}]}'
551
+ '{"user.email": [{"type": "eq", "value": "user@example.com"}]}',
543
552
  ],
544
553
  constraints: [
545
554
  "Time filters support ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h)",
546
555
  "ISO 8601 times must be in UTC and use extended format",
547
- "Relative time periods: h (hours), d (days)"
548
- ]
556
+ "Relative time periods: h (hours), d (days)",
557
+ ],
549
558
  },
550
559
  {
551
560
  name: "sort",
552
- type: z.enum(["first_seen", "last_seen", "events", "users", "unsorted"]).default("last_seen"),
561
+ type: z
562
+ .enum(["first_seen", "last_seen", "events", "users", "unsorted"])
563
+ .default("last_seen"),
553
564
  description: "Field to sort the errors by",
554
565
  required: false,
555
- examples: ["last_seen"]
566
+ examples: ["last_seen"],
556
567
  },
557
568
  {
558
569
  name: "direction",
559
570
  type: z.enum(["asc", "desc"]).default("desc"),
560
571
  description: "Sort direction for ordering results",
561
572
  required: false,
562
- examples: ["desc"]
573
+ examples: ["desc"],
563
574
  },
564
575
  {
565
576
  name: "per_page",
566
577
  type: z.number().min(1).max(100).default(30),
567
578
  description: "How many results to return per page.",
568
579
  required: false,
569
- examples: ["30", "50", "100"]
580
+ examples: ["30", "50", "100"],
570
581
  },
571
582
  {
572
583
  name: "next",
573
584
  type: z.string().url(),
574
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.",
575
586
  required: false,
576
- examples: ["https://api.bugsnag.com/projects/515fb9337c1074f6fd000003/errors?offset=30&per_page=30&sort=last_seen"],
577
- constraints: ["Only values provided in the output from this tool can be used. Do not attempt to construct it manually."]
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
+ ],
578
593
  },
579
- ...(this.projectApiKey ? [] : [
580
- {
581
- name: "projectId",
582
- type: z.string(),
583
- description: "ID of the project to query for errors",
584
- required: true,
585
- }
586
- ])
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
+ ]),
587
604
  ],
588
605
  examples: [
589
606
  {
590
607
  description: "Find errors affecting a specific user in the last 24 hours",
591
608
  parameters: {
592
609
  filters: {
593
- "user.email": [{ "type": "eq", "value": "user@example.com" }],
594
- "event.since": [{ "type": "eq", "value": "24h" }]
595
- }
610
+ "user.email": [{ type: "eq", value: "user@example.com" }],
611
+ "event.since": [{ type: "eq", value: "24h" }],
612
+ },
596
613
  },
597
- 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",
598
615
  },
599
616
  {
600
617
  description: "Get the 10 open errors with the most users affected in the last 30 days",
601
618
  parameters: {
602
619
  filters: {
603
- "event.since": [{ "type": "eq", "value": "30d" }],
604
- "error.status": [{ "type": "eq", "value": "open" }]
620
+ "event.since": [{ type: "eq", value: "30d" }],
621
+ "error.status": [{ type: "eq", value: "open" }],
605
622
  },
606
623
  sort: "users",
607
624
  direction: "desc",
608
- per_page: 10
625
+ per_page: 10,
609
626
  },
610
- 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",
611
628
  },
612
629
  {
613
630
  description: "Get the next 50 results",
614
631
  parameters: {
615
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",
616
- per_page: 50
633
+ per_page: 50,
617
634
  },
618
- 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"
619
- }
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
+ },
620
637
  ],
621
638
  hints: [
622
639
  "Use list_project_event_filters tool first to discover valid filter field names for your project",
@@ -627,14 +644,14 @@ export class BugsnagClient {
627
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",
628
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.",
629
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.",
630
- "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."
631
- ]
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
+ ],
632
649
  }, async (args, _extra) => {
633
650
  const project = await this.getInputProject(args.projectId);
634
651
  // Validate filter keys against cached event fields
635
652
  if (args.filters) {
636
653
  const eventFields = this.cache.get(cacheKeys.CURRENT_PROJECT_EVENT_FILTERS) || [];
637
- const validKeys = new Set(eventFields.map(f => f.display_id));
654
+ const validKeys = new Set(eventFields.map((f) => f.display_id));
638
655
  for (const key of Object.keys(args.filters)) {
639
656
  if (!validKeys.has(key)) {
640
657
  throw new Error(`Invalid filter key: ${key}`);
@@ -642,11 +659,11 @@ export class BugsnagClient {
642
659
  }
643
660
  }
644
661
  const defaultFilters = {
645
- "event.since": [{ "type": "eq", "value": "30d" }],
646
- "error.status": [{ "type": "eq", "value": "open" }]
662
+ "event.since": [{ type: "eq", value: "30d" }],
663
+ "error.status": [{ type: "eq", value: "open" }],
647
664
  };
648
665
  const options = {
649
- filters: { ...defaultFilters, ...args.filters }
666
+ filters: { ...defaultFilters, ...args.filters },
650
667
  };
651
668
  if (args.sort !== undefined)
652
669
  options.sort = args.sort;
@@ -658,12 +675,12 @@ export class BugsnagClient {
658
675
  options.next = args.next;
659
676
  const response = await this.errorsApi.listProjectErrors(project.id, options);
660
677
  const errors = response.body || [];
661
- const totalCount = response.headers.get('X-Total-Count');
662
- const linkHeader = response.headers.get('Link');
678
+ const totalCount = response.headers.get("X-Total-Count");
679
+ const linkHeader = response.headers.get("Link");
663
680
  const result = {
664
681
  data: errors,
665
682
  count: errors.length,
666
- total: totalCount ? parseInt(totalCount) : undefined,
683
+ total: totalCount ? parseInt(totalCount, 10) : undefined,
667
684
  next: linkHeader?.match(/<([^>]+)>/)?.[1],
668
685
  };
669
686
  return {
@@ -677,20 +694,20 @@ export class BugsnagClient {
677
694
  useCases: [
678
695
  "Discover what filter fields are available before searching for errors",
679
696
  "Find the correct field names for filtering by user, environment, or custom metadata",
680
- "Understand filter options and data types for building complex queries"
697
+ "Understand filter options and data types for building complex queries",
681
698
  ],
682
699
  parameters: [],
683
700
  examples: [
684
701
  {
685
702
  description: "Get all available filter fields",
686
703
  parameters: {},
687
- expectedOutput: "JSON array of EventField objects containing display_id, custom flag, and filter/pivot options"
688
- }
704
+ expectedOutput: "JSON array of EventField objects containing display_id, custom flag, and filter/pivot options",
705
+ },
689
706
  ],
690
707
  hints: [
691
708
  "Use this tool before the List Errors or Get Error tools to understand available filters",
692
- "Look for display_id field in the response - these are the field names to use in filters"
693
- ]
709
+ "Look for display_id field in the response - these are the field names to use in filters",
710
+ ],
694
711
  }, async (_args, _extra) => {
695
712
  const projectFields = this.cache.get(cacheKeys.CURRENT_PROJECT_EVENT_FILTERS);
696
713
  if (!projectFields)
@@ -706,52 +723,54 @@ export class BugsnagClient {
706
723
  useCases: [
707
724
  "Mark an error as open, fixed or ignored",
708
725
  "Discard or un-discard an error",
709
- "Update the severity of an error"
726
+ "Update the severity of an error",
710
727
  ],
711
728
  parameters: [
712
- ...(this.projectApiKey ? [] : [
713
- {
714
- name: "projectId",
715
- type: z.string(),
716
- description: "ID of the project that contains the error to be updated",
717
- required: true,
718
- }
719
- ]),
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
+ ]),
720
739
  {
721
740
  name: "errorId",
722
741
  type: z.string(),
723
742
  description: "ID of the error to update",
724
743
  required: true,
725
- examples: ["6863e2af8c857c0a5023b411"]
744
+ examples: ["6863e2af8c857c0a5023b411"],
726
745
  },
727
746
  {
728
747
  name: "operation",
729
748
  type: z.enum(PERMITTED_UPDATE_OPERATIONS),
730
749
  description: "The operation to apply to the error",
731
750
  required: true,
732
- examples: ["fix", "open", "ignore", "discard", "undiscard"]
733
- }
751
+ examples: ["fix", "open", "ignore", "discard", "undiscard"],
752
+ },
734
753
  ],
735
754
  examples: [
736
755
  {
737
756
  description: "Mark an error as fixed",
738
757
  parameters: {
739
758
  errorId: "6863e2af8c857c0a5023b411",
740
- operation: "fix"
759
+ operation: "fix",
741
760
  },
742
- expectedOutput: "Success response indicating the error was marked as fixed"
743
- }
761
+ expectedOutput: "Success response indicating the error was marked as fixed",
762
+ },
744
763
  ],
745
764
  hints: [
746
- "Only use valid operations - BugSnag may reject invalid values"
765
+ "Only use valid operations - BugSnag may reject invalid values",
747
766
  ],
748
767
  readOnly: false,
749
768
  idempotent: false,
750
769
  }, async (args, _extra) => {
751
770
  const { errorId, operation } = args;
752
771
  const project = await this.getInputProject(args.projectId);
753
- let severity = undefined;
754
- if (operation === 'override_severity') {
772
+ let severity;
773
+ if (operation === "override_severity") {
755
774
  // illicit the severity from the user
756
775
  const result = await getInput({
757
776
  message: "Please provide the new severity for the error (e.g. 'info', 'warning', 'error', 'critical')",
@@ -760,20 +779,24 @@ export class BugsnagClient {
760
779
  properties: {
761
780
  severity: {
762
781
  type: "string",
763
- enum: ['info', 'warning', 'error'],
764
- description: "The new severity level for the error"
765
- }
766
- }
782
+ enum: ["info", "warning", "error"],
783
+ description: "The new severity level for the error",
784
+ },
785
+ },
767
786
  },
768
- required: ["severity"]
787
+ required: ["severity"],
769
788
  });
770
789
  if (result.action === "accept" && result.content?.severity) {
771
790
  severity = result.content.severity;
772
791
  }
773
792
  }
774
- const result = await this.updateError(project.id, errorId, operation, { severity });
793
+ const result = await this.updateError(project.id, errorId, operation, {
794
+ severity,
795
+ });
775
796
  return {
776
- content: [{ type: "text", text: JSON.stringify({ success: result }) }],
797
+ content: [
798
+ { type: "text", text: JSON.stringify({ success: result }) },
799
+ ],
777
800
  };
778
801
  });
779
802
  register({
@@ -831,7 +854,7 @@ export class BugsnagClient {
831
854
  nextUrl: "/projects/515fb9337c1074f6fd000003/builds?offset=30&per_page=30",
832
855
  },
833
856
  expectedOutput: "JSON array of build objects with metadata from the next page",
834
- }
857
+ },
835
858
  ],
836
859
  hints: ["For more detailed results use the Get Build tool"],
837
860
  readOnly: true,
@@ -851,7 +874,7 @@ export class BugsnagClient {
851
874
  builds,
852
875
  next: nextUrl,
853
876
  }),
854
- }
877
+ },
855
878
  ],
856
879
  };
857
880
  });
@@ -1091,10 +1114,12 @@ export class BugsnagClient {
1091
1114
  registerResources(register) {
1092
1115
  register("event", "{id}", async (uri, variables, _extra) => {
1093
1116
  return {
1094
- contents: [{
1117
+ contents: [
1118
+ {
1095
1119
  uri: uri.href,
1096
- text: JSON.stringify(await this.getEvent(variables.id))
1097
- }]
1120
+ text: JSON.stringify(await this.getEvent(variables.id)),
1121
+ },
1122
+ ],
1098
1123
  };
1099
1124
  });
1100
1125
  }