@smartbear/mcp 0.4.0 → 0.5.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 CHANGED
@@ -18,7 +18,7 @@
18
18
  </div>
19
19
  <br />
20
20
 
21
- A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [Insight Hub](https://www.smartbear.com/insight-hub), [Reflect](https://reflect.run), [API Hub](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/) and [Pact Broker](https://docs.pact.io/)
21
+ A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [BugSnag](https://www.bugsnag.com/), [Reflect](https://reflect.run), [API Hub](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/) and [Pact Broker](https://docs.pact.io/)
22
22
 
23
23
  ## What is MCP?
24
24
 
@@ -28,7 +28,7 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction)
28
28
 
29
29
  See individual guides for suggested prompts and supported tools and resources:
30
30
 
31
- - [Insight Hub](https://developer.smartbear.com/smartbear-mcp/docs/insight-hub-integration) - Comprehensive error monitoring and debugging capabilities
31
+ - [BugSnag](https://developer.smartbear.com/smartbear-mcp/docs/bugsnag-integration) - Comprehensive error monitoring and debugging capabilities
32
32
  - [Test Hub](https://developer.smartbear.com/smartbear-mcp/docs/test-hub-integration) - Test management and execution capabilities
33
33
  - [API Hub](https://developer.smartbear.com/smartbear-mcp/docs/api-hub-integration) - Portal management capabilities
34
34
  - [PactFlow](https://developer.smartbear.com/pactflow/default/getting-started) - Contract testing capabilities
@@ -37,14 +37,14 @@ See individual guides for suggested prompts and supported tools and resources:
37
37
  ## Prerequisites
38
38
 
39
39
  - Node.js 20+ and npm
40
- - Access to SmartBear products (Insight Hub, Reflect, or API Hub)
40
+ - Access to SmartBear products (BugSnag, Reflect, or API Hub)
41
41
  - Valid API tokens for the products you want to integrate
42
42
 
43
43
  ## Installation
44
44
 
45
45
  The MCP server is distributed as an npm package [`@smartbear/mcp`](https://www.npmjs.com/package/@smartbear/mcp), making it easy to integrate into your development workflow.
46
46
 
47
- The server is started with the API key or auth token that you use with your SmartBear product(s). They are optional and can be removed from your configuration if you aren't using the product. For Insight Hub, if you provide a project API key it will narrow down all searches to a single project in your Insight Hub dashboard. Leave this field blank if you wish to interact across multiple projects at a time.
47
+ The server is started with the API key or auth token that you use with your SmartBear product(s). They are optional and can be removed from your configuration if you aren't using the product. For BugSnag, if you provide a project API key it will narrow down all searches to a single project in your BugSnag dashboard. Leave this field blank if you wish to interact across multiple projects at a time.
48
48
 
49
49
  ### VS Code with Copilot
50
50
 
@@ -66,8 +66,8 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
66
66
  "@smartbear/mcp@latest"
67
67
  ],
68
68
  "env": {
69
- "INSIGHT_HUB_AUTH_TOKEN": "${input:insight_hub_auth_token}",
70
- "INSIGHT_HUB_PROJECT_API_KEY": "${input:insight_hub_project_api_key}",
69
+ "BUGSNAG_AUTH_TOKEN": "${input:bugsnag_auth_token}",
70
+ "BUGSNAG_PROJECT_API_KEY": "${input:bugsnag_project_api_key}",
71
71
  "REFLECT_API_TOKEN": "${input:reflect_api_token}",
72
72
  "API_HUB_API_KEY": "${input:api_hub_api_key}",
73
73
  "PACT_BROKER_BASE_URL": "${input:pact_broker_base_url}",
@@ -79,15 +79,15 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
79
79
  },
80
80
  "inputs": [
81
81
  {
82
- "id": "insight_hub_auth_token",
82
+ "id": "bugsnag_auth_token",
83
83
  "type": "promptString",
84
- "description": "Insight Hub Auth Token - leave blank to disable Insight Hub tools",
84
+ "description": "BugSnag Auth Token - leave blank to disable BugSnag tools",
85
85
  "password": true
86
86
  },
87
87
  {
88
- "id": "insight_hub_project_api_key",
88
+ "id": "bugsnag_project_api_key",
89
89
  "type": "promptString",
90
- "description": "Insight Hub Project API Key - for single project interactions",
90
+ "description": "BugSnag Project API Key - for single project interactions",
91
91
  "password": false
92
92
  },
93
93
  {
@@ -145,8 +145,8 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
145
145
  "@smartbear/mcp@latest"
146
146
  ],
147
147
  "env": {
148
- "INSIGHT_HUB_AUTH_TOKEN": "your_personal_auth_token",
149
- "INSIGHT_HUB_PROJECT_API_KEY": "your_project_api_key",
148
+ "BUGSNAG_AUTH_TOKEN": "your_personal_auth_token",
149
+ "BUGSNAG_PROJECT_API_KEY": "your_project_api_key",
150
150
  "REFLECT_API_TOKEN": "your_reflect_token",
151
151
  "API_HUB_API_KEY": "your_api_hub_key",
152
152
  "PACT_BROKER_BASE_URL": "your_pactflow_or_pactbroker_base_url",
@@ -202,8 +202,8 @@ For developers who want to contribute to the SmartBear MCP server, customize its
202
202
  },
203
203
  "args": ["<PATH_TO_SMARTBEAR_MCP_REPO>/dist/index.js"],
204
204
  "env": {
205
- "INSIGHT_HUB_AUTH_TOKEN": "${input:insight_hub_auth_token}",
206
- "INSIGHT_HUB_PROJECT_API_KEY": "${input:insight_hub_project_api_key}",
205
+ "BUGSNAG_AUTH_TOKEN": "${input:bugsnag_auth_token}",
206
+ "BUGSNAG_PROJECT_API_KEY": "${input:bugsnag_project_api_key}",
207
207
  "REFLECT_API_TOKEN": "${input:reflect_api_token}",
208
208
  "API_HUB_API_KEY": "${input:api_hub_api_key}",
209
209
  "PACT_BROKER_BASE_URL": "${input:pact_broker_base_url}",
@@ -215,15 +215,15 @@ For developers who want to contribute to the SmartBear MCP server, customize its
215
215
  },
216
216
  "inputs": [
217
217
  {
218
- "id": "insight_hub_auth_token",
218
+ "id": "bugsnag_auth_token",
219
219
  "type": "promptString",
220
- "description": "Insight Hub Auth Token - leave blank to disable Insight Hub tools",
220
+ "description": "BugSnag Auth Token - leave blank to disable BugSnag tools",
221
221
  "password": true
222
222
  },
223
223
  {
224
- "id": "insight_hub_project_api_key",
224
+ "id": "bugsnag_project_api_key",
225
225
  "type": "promptString",
226
- "description": "Insight Hub Project API Key - for single project interactions",
226
+ "description": "BugSnag Project API Key - for single project interactions",
227
227
  "password": false
228
228
  },
229
229
  {
@@ -270,7 +270,7 @@ For developers who want to contribute to the SmartBear MCP server, customize its
270
270
  5. **Local testing** using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) web interface:
271
271
 
272
272
  ```bash
273
- INSIGHT_HUB_AUTH_TOKEN="your_token" REFLECT_API_TOKEN="your_reflect_token" API_HUB_API_KEY="your_api_hub_key" PACT_BROKER_BASE_URL="your-pactflow-url" PACT_BROKER_TOKEN="your-pactflow-token" PACT_BROKER_USERNAME="your-pact-broker-username" PACT_BROKER_PASSWORD="your-pact-broker-password" npx @modelcontextprotocol/inspector npx @smartbear/mcp@latest
273
+ BUGSNAG_AUTH_TOKEN="your_token" REFLECT_API_TOKEN="your_reflect_token" API_HUB_API_KEY="your_api_hub_key" PACT_BROKER_BASE_URL="your-pactflow-url" PACT_BROKER_TOKEN="your-pactflow-token" PACT_BROKER_USERNAME="your-pact-broker-username" PACT_BROKER_PASSWORD="your-pact-broker-password" npx @modelcontextprotocol/inspector npx @smartbear/mcp@latest
274
274
  ```
275
275
 
276
276
  ## License
@@ -285,4 +285,4 @@ This MCP server is licensed under the MIT License. This means you are free to us
285
285
 
286
286
  ---
287
287
 
288
- **SmartBear MCP Server** - Bringing the power of SmartBear's testing and monitoring ecosystem to your AI-powered development workflow.
288
+ **SmartBear MCP Server** - Bringing the power of SmartBear's testing and monitoring ecosystem to your AI-powered development workflow.
@@ -1,10 +1,11 @@
1
1
  import { BaseAPI, pickFieldsFromArray } from './base.js';
2
2
  // --- API Class ---
3
3
  export class CurrentUserAPI extends BaseAPI {
4
+ static filterFields = ["collaborators_url", "projects_url", "upgrade_url"];
4
5
  static organizationFields = ['id', 'name', 'slug'];
5
6
  static projectFields = ['id', 'name', 'slug', 'api_key'];
6
7
  constructor(configuration) {
7
- super(configuration);
8
+ super(configuration, CurrentUserAPI.filterFields);
8
9
  }
9
10
  /**
10
11
  * List the current user's organizations
@@ -22,8 +22,9 @@ export const ReopenConditions = [
22
22
  ];
23
23
  // --- API Class ---
24
24
  export class ErrorAPI extends BaseAPI {
25
+ static filterFields = ["url", "project_url", "events_url"];
25
26
  constructor(configuration) {
26
- super(configuration);
27
+ super(configuration, ErrorAPI.filterFields);
27
28
  }
28
29
  /**
29
30
  * View an Error on a Project
@@ -95,9 +96,41 @@ export class ErrorAPI extends BaseAPI {
95
96
  * GET /projects/{project_id}/errors
96
97
  */
97
98
  async listProjectErrors(projectId, options = {}) {
98
- const url = options.filters
99
- ? `/projects/${projectId}/errors?${toQueryString(options.filters)}`
100
- : `/projects/${projectId}/errors`;
99
+ let url = `/projects/${projectId}/errors`;
100
+ // Next links need to be used as-is to ensure results are consistent, so only the page size can be modified
101
+ if (options.next !== undefined) {
102
+ const nextUrl = new URL(options.next);
103
+ if (options.per_page !== undefined) {
104
+ nextUrl.searchParams.set('per_page', options.per_page.toString());
105
+ }
106
+ url = nextUrl.toString();
107
+ }
108
+ else {
109
+ const params = new URLSearchParams();
110
+ // Add filter parameters
111
+ if (options.filters) {
112
+ const filterParams = new URLSearchParams(toQueryString(options.filters));
113
+ filterParams.forEach((value, key) => {
114
+ params.append(key, value);
115
+ });
116
+ }
117
+ // Add pagination and sorting parameters
118
+ if (options.base !== undefined) {
119
+ params.append('base', options.base);
120
+ }
121
+ if (options.sort !== undefined) {
122
+ params.append('sort', options.sort);
123
+ }
124
+ if (options.direction !== undefined) {
125
+ params.append('direction', options.direction);
126
+ }
127
+ if (options.per_page !== undefined) {
128
+ params.append('per_page', options.per_page.toString());
129
+ }
130
+ if (params.size > 0) {
131
+ url = `/projects/${projectId}/errors?${params}`;
132
+ }
133
+ }
101
134
  return (await this.request({
102
135
  method: 'GET',
103
136
  url,
@@ -1,6 +1,7 @@
1
1
  import { BaseAPI, pickFieldsFromArray } from "./base.js";
2
2
  // --- API Class ---
3
3
  export class ProjectAPI extends BaseAPI {
4
+ static filterFields = ["errors_url", "events_url", "url", "html_url"];
4
5
  static eventFieldFields = [
5
6
  'custom',
6
7
  'display_id',
@@ -8,7 +9,7 @@ export class ProjectAPI extends BaseAPI {
8
9
  'pivot_options'
9
10
  ];
10
11
  constructor(configuration) {
11
- super(configuration);
12
+ super(configuration, ProjectAPI.filterFields);
12
13
  }
13
14
  /**
14
15
  * List the Event Fields for a Project
@@ -14,8 +14,10 @@ export function pickFieldsFromArray(arr, keys) {
14
14
  }
15
15
  export class BaseAPI {
16
16
  configuration;
17
- constructor(configuration) {
17
+ filterFields;
18
+ constructor(configuration, filterFields) {
18
19
  this.configuration = configuration;
20
+ this.filterFields = filterFields || [];
19
21
  }
20
22
  async request(options, paginate = false) {
21
23
  const headers = {
@@ -61,6 +63,21 @@ export class BaseAPI {
61
63
  if (paginate) {
62
64
  apiResponse.body = results;
63
65
  }
66
+ if (Array.isArray(apiResponse.body)) {
67
+ apiResponse.body.forEach(this.sanitizeResponse.bind(this));
68
+ }
69
+ else {
70
+ this.sanitizeResponse(apiResponse.body ?? {});
71
+ }
64
72
  return apiResponse;
65
73
  }
74
+ sanitizeResponse(data) {
75
+ if (!data)
76
+ return;
77
+ for (const key of this.filterFields) {
78
+ if (key in data) {
79
+ delete data[key];
80
+ }
81
+ }
82
+ }
66
83
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Filters utility for Insight Hub API
2
+ * Filters utility for BugSnag API
3
3
  *
4
4
  * This file provides utility functions for creating filter URL parameters
5
- * based on the Insight Hub filtering specification described in the Filtering.md document.
5
+ * based on the BugSnag filtering specification described in the Filtering.md document.
6
6
  */
7
7
  import { z } from "zod";
8
8
  export const FilterValueSchema = z.object({
@@ -6,12 +6,12 @@ import { ProjectAPI } from "./client/api/Project.js";
6
6
  import { FilterObjectSchema, toQueryString } from "./client/api/filters.js";
7
7
  const HUB_PREFIX = "00000";
8
8
  const DEFAULT_DOMAIN = "bugsnag.com";
9
- const HUB_DOMAIN = "insighthub.smartbear.com";
9
+ const HUB_DOMAIN = "bugsnag.smartbear.com";
10
10
  const cacheKeys = {
11
- ORG: "insight_hub_org",
12
- PROJECTS: "insight_hub_projects",
13
- CURRENT_PROJECT: "insight_hub_current_project",
14
- CURRENT_PROJECT_EVENT_FILTERS: "insight_hub_current_project_event_filters",
11
+ ORG: "bugsnag_org",
12
+ PROJECTS: "bugsnag_projects",
13
+ CURRENT_PROJECT: "bugsnag_current_project",
14
+ CURRENT_PROJECT_EVENT_FILTERS: "bugsnag_current_project_event_filters",
15
15
  };
16
16
  // Exclude certain event fields from the project event filters to improve agent usage
17
17
  const EXCLUDED_EVENT_FIELDS = new Set([
@@ -25,7 +25,7 @@ const PERMITTED_UPDATE_OPERATIONS = [
25
25
  "discard",
26
26
  "undiscard"
27
27
  ];
28
- export class InsightHubClient {
28
+ export class BugsnagClient {
29
29
  currentUserApi;
30
30
  errorsApi;
31
31
  cache;
@@ -33,8 +33,8 @@ export class InsightHubClient {
33
33
  projectApiKey;
34
34
  apiEndpoint;
35
35
  appEndpoint;
36
- name = "Insight Hub";
37
- prefix = "insight_hub";
36
+ name = "Bugsnag";
37
+ prefix = "bugsnag";
38
38
  constructor(token, projectApiKey, endpoint) {
39
39
  this.apiEndpoint = this.getEndpoint("api", projectApiKey, endpoint);
40
40
  this.appEndpoint = this.getEndpoint("app", projectApiKey, endpoint);
@@ -318,7 +318,7 @@ export class InsightHubClient {
318
318
  }
319
319
  ],
320
320
  hints: [
321
- "Error IDs can be found using the List Errors tool",
321
+ "Error IDs can be found using the List Project Errors tool",
322
322
  "Use this after filtering errors to get detailed information about specific errors",
323
323
  "If you used a filter to get this error, you can pass the same filters here to restrict the results or apply further filters",
324
324
  "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",
@@ -378,7 +378,7 @@ export class InsightHubClient {
378
378
  {
379
379
  name: "link",
380
380
  type: z.string(),
381
- description: "Full URL to the event details page in the Insight Hub dashboard (web interface)",
381
+ description: "Full URL to the event details page in the BugSnag dashboard (web interface)",
382
382
  required: true,
383
383
  examples: [
384
384
  "https://app.bugsnag.com/my-org/my-project/errors/6863e2af8c857c0a5023b411?event_id=6863e2af012caf1d5c320000"
@@ -399,7 +399,7 @@ export class InsightHubClient {
399
399
  ],
400
400
  hints: [
401
401
  "The URL must contain both project slug in the path and event_id in query parameters",
402
- "This is useful when users share Insight Hub dashboard URLs and you need to extract the event data"
402
+ "This is useful when users share BugSnag dashboard URLs and you need to extract the event data"
403
403
  ]
404
404
  }, async (args, _extra) => {
405
405
  if (!args.link)
@@ -423,7 +423,7 @@ export class InsightHubClient {
423
423
  // Dynamically infer the filters schema from cached project event fields
424
424
  register({
425
425
  title: "List Project Errors",
426
- summary: "List and search errors in a project using customizable filters",
426
+ summary: "List and search errors in a project using customizable filters and pagination",
427
427
  purpose: "Retrieve filtered list of errors from a project for analysis, debugging, and reporting",
428
428
  useCases: [
429
429
  "Debug recent application errors by filtering for open errors in the last 7 days",
@@ -435,7 +435,7 @@ export class InsightHubClient {
435
435
  {
436
436
  name: "filters",
437
437
  type: FilterObjectSchema,
438
- description: "Apply filters to narrow down the error list. Use the List Errors or Get Error tools to discover available filter fields",
438
+ description: "Apply filters to narrow down the error list. Use the List Project Event Filters tool to discover available filter fields",
439
439
  required: false,
440
440
  examples: [
441
441
  '{"error.status": [{"type": "eq", "value": "open"}]}',
@@ -449,6 +449,35 @@ export class InsightHubClient {
449
449
  "Relative time periods: h (hours), d (days)"
450
450
  ]
451
451
  },
452
+ {
453
+ name: "sort",
454
+ type: z.enum(["first_seen", "last_seen", "events", "users", "unsorted"]),
455
+ description: "Field to sort the errors by (default: last_seen)",
456
+ required: false,
457
+ examples: ["last_seen"]
458
+ },
459
+ {
460
+ name: "direction",
461
+ type: z.enum(["asc", "desc"]).default("desc"),
462
+ description: "Sort direction for ordering results",
463
+ required: false,
464
+ examples: ["desc"]
465
+ },
466
+ {
467
+ name: "per_page",
468
+ type: z.number().min(1).max(100),
469
+ description: "How many results to return per page.",
470
+ required: false,
471
+ examples: ["30", "50", "100"]
472
+ },
473
+ {
474
+ name: "next",
475
+ type: z.string().url(),
476
+ description: "URL for retrieving the next page of results. Use the value in the previous response to get the next page when more results are available.",
477
+ required: false,
478
+ examples: ["https://api.bugsnag.com/projects/515fb9337c1074f6fd000003/errors?offset=30&per_page=30&sort=last_seen"],
479
+ constraints: ["Only values provided in the output from this tool can be used. Do not attempt to construct it manually."]
480
+ },
452
481
  ...(this.projectApiKey ? [] : [
453
482
  {
454
483
  name: "projectId",
@@ -467,7 +496,28 @@ export class InsightHubClient {
467
496
  "event.since": [{ "type": "eq", "value": "24h" }]
468
497
  }
469
498
  },
470
- expectedOutput: "JSON object with a list of errors in the 'data' field, and an error count in the 'count' field"
499
+ expectedOutput: "JSON object with a list of errors in the 'data' field, a count of the current page of results in the 'count' field, and a total count of all results in the 'total' field"
500
+ },
501
+ {
502
+ description: "Get the 10 open errors with the most users affected in the last 30 days",
503
+ parameters: {
504
+ filters: {
505
+ "event.since": [{ "type": "eq", "value": "30d" }],
506
+ "error.status": [{ "type": "eq", "value": "open" }]
507
+ },
508
+ sort: "users",
509
+ direction: "desc",
510
+ per_page: 10
511
+ },
512
+ expectedOutput: "JSON object with a list of errors in the 'data' field, a count of the current page of results in the 'count' field, and a total count of all results in the 'total' field"
513
+ },
514
+ {
515
+ description: "Get the next 50 results",
516
+ parameters: {
517
+ next: "https://api.bugsnag.com/projects/515fb9337c1074f6fd000003/errors?base=2025-08-29T13%3A11%3A37Z&direction=desc&filters%5Berror.status%5D%5B%5D%5Btype%5D=eq&filters%5Berror.status%5D%5B%5D%5Bvalue%5D=open&offset=10&per_page=10&sort=users",
518
+ per_page: 50
519
+ },
520
+ expectedOutput: "JSON object with a list of errors in the 'data' field, a count of the current page of results in the 'count' field, and a total count of all results in the 'total' field"
471
521
  }
472
522
  ],
473
523
  hints: [
@@ -475,13 +525,16 @@ export class InsightHubClient {
475
525
  "Combine multiple filters to narrow results - filters are applied with AND logic",
476
526
  "For time filters: use relative format (7d, 24h) for recent periods or ISO 8601 UTC format (2018-05-20T00:00:00Z) for specific dates",
477
527
  "Common time filters: event.since (from this time), event.before (until this time)",
478
- "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"
528
+ "There may not be any errors matching the filters - this is not a problem with the tool, in fact it might be a good thing that the user's application had no errors",
529
+ "This tool returns paged results. The 'count' field indicates the number of results returned in the current page, and the 'total' field indicates the total number of results across all pages.",
530
+ "If the output contains a 'next' value, there are more results available - call this tool again supplying the next URL as a parameter to retrieve the next page.",
531
+ "Do not modify the next URL as this can cause incorrect results. The only other parameter that can be used with 'next' is 'per_page' to control the page size."
479
532
  ]
480
533
  }, async (args, _extra) => {
481
534
  const project = await this.getInputProject(args.projectId);
482
- // Optionally, validate filter keys against cached event fields
483
- const eventFields = this.cache.get(cacheKeys.CURRENT_PROJECT_EVENT_FILTERS) || [];
535
+ // Validate filter keys against cached event fields
484
536
  if (args.filters) {
537
+ const eventFields = this.cache.get(cacheKeys.CURRENT_PROJECT_EVENT_FILTERS) || [];
485
538
  const validKeys = new Set(eventFields.map(f => f.display_id));
486
539
  for (const key of Object.keys(args.filters)) {
487
540
  if (!validKeys.has(key)) {
@@ -489,11 +542,26 @@ export class InsightHubClient {
489
542
  }
490
543
  }
491
544
  }
492
- const response = await this.errorsApi.listProjectErrors(project.id, { filters: args.filters });
545
+ const options = {};
546
+ if (args.filters)
547
+ options.filters = args.filters;
548
+ if (args.sort !== undefined)
549
+ options.sort = args.sort;
550
+ if (args.direction !== undefined)
551
+ options.direction = args.direction;
552
+ if (args.per_page !== undefined)
553
+ options.per_page = args.per_page;
554
+ if (args.next !== undefined)
555
+ options.next = args.next;
556
+ const response = await this.errorsApi.listProjectErrors(project.id, options);
493
557
  const errors = response.body || [];
558
+ const totalCount = response.headers.get('X-Total-Count');
559
+ const linkHeader = response.headers.get('Link');
494
560
  const result = {
495
561
  data: errors,
496
562
  count: errors.length,
563
+ total: totalCount ? parseInt(totalCount) : undefined,
564
+ next: linkHeader?.match(/<([^>]+)>/)?.[1],
497
565
  };
498
566
  return {
499
567
  content: [{ type: "text", text: JSON.stringify(result) }],
@@ -572,7 +640,7 @@ export class InsightHubClient {
572
640
  }
573
641
  ],
574
642
  hints: [
575
- "Only use valid operations - Insight Hub may reject invalid values"
643
+ "Only use valid operations - BugSnag may reject invalid values"
576
644
  ],
577
645
  readOnly: false,
578
646
  idempotent: false,
@@ -1,3 +1,3 @@
1
- import packageJson from '../package.json' with { type: "json" };
1
+ import packageJson from '../../package.json' with { type: "json" };
2
2
  export const MCP_SERVER_NAME = packageJson.config.mcpServerName;
3
3
  export const MCP_SERVER_VERSION = packageJson.version;
@@ -96,10 +96,9 @@ export class SmartBearMcpServer extends McpServer {
96
96
  }
97
97
  if (zodSchema && zodSchema instanceof ZodObject) {
98
98
  description += "\n\n**Parameters:**\n";
99
- for (const key of Object.keys(zodSchema.shape)) {
100
- const field = zodSchema.shape[key];
101
- description += this.formatParameterDescription(key, field);
102
- }
99
+ description += Object.keys(zodSchema.shape)
100
+ .map(key => this.formatParameterDescription(key, zodSchema.shape[key]))
101
+ .join('\n');
103
102
  }
104
103
  if (outputFormat) {
105
104
  description += `\n\n**Output Format:** ${outputFormat}`;
@@ -119,7 +118,11 @@ export class SmartBearMcpServer extends McpServer {
119
118
  return description.trim();
120
119
  }
121
120
  formatParameterDescription(key, field) {
122
- return `- ${key} (${this.getReadableTypeName(field)})${field.isOptional() ? '' : ' *required*'}${field.description ? `: ${field.description}` : ''}${key === "examples" && field instanceof ZodEnum ? ` (e.g. ${Object.keys(field.enum).join(', ')})` : ''}${key === "constraints" && field instanceof ZodEnum ? `\n - ${Object.keys(field.enum).join('\n - ')}` : ''} \n`;
121
+ return `- ${key} (${this.getReadableTypeName(field)})` +
122
+ `${field.isOptional() ? '' : ' *required*'}` +
123
+ `${field.description ? `: ${field.description}` : ''}` +
124
+ `${key === "examples" && field instanceof ZodEnum ? ` (e.g. ${Object.keys(field.enum).join(', ')})` : ''}` +
125
+ `${key === "constraints" && field instanceof ZodEnum ? `\n - ${Object.keys(field.enum).join('\n - ')}` : ''}`;
123
126
  }
124
127
  getReadableTypeName(zodType) {
125
128
  if (zodType instanceof ZodString)
package/dist/index.js CHANGED
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import Bugsnag from "./common/bugsnag.js";
4
- import { InsightHubClient } from "./insight-hub/client.js";
4
+ import { BugsnagClient } from "./bugsnag/client.js";
5
5
  import { ReflectClient } from "./reflect/client.js";
6
6
  import { ApiHubClient } from "./api-hub/client.js";
7
7
  import { SmartBearMcpServer } from "./common/server.js";
8
8
  import { PactflowClient } from "./pactflow/client.js";
9
9
  // This is used to report errors in the MCP server itself
10
- // If you want to use your own BugSnag API key, set the MCP_SERVER_INSIGHT_HUB_API_KEY environment variable
11
- const McpServerBugsnagAPIKey = process.env.MCP_SERVER_INSIGHT_HUB_API_KEY;
10
+ // If you want to use your own BugSnag API key, set the MCP_SERVER_BUGSNAG_API_KEY environment variable
11
+ const McpServerBugsnagAPIKey = process.env.MCP_SERVER_BUGSNAG_API_KEY;
12
12
  if (McpServerBugsnagAPIKey) {
13
13
  Bugsnag.start(McpServerBugsnagAPIKey);
14
14
  }
15
15
  async function main() {
16
16
  const server = new SmartBearMcpServer();
17
17
  const reflectToken = process.env.REFLECT_API_TOKEN;
18
- const insightHubToken = process.env.INSIGHT_HUB_AUTH_TOKEN;
18
+ const bugsnagToken = process.env.BUGSNAG_AUTH_TOKEN;
19
19
  const apiHubToken = process.env.API_HUB_API_KEY;
20
20
  const pactBrokerToken = process.env.PACT_BROKER_TOKEN;
21
21
  const pactBrokerUrl = process.env.PACT_BROKER_BASE_URL;
@@ -26,10 +26,10 @@ async function main() {
26
26
  server.addClient(new ReflectClient(reflectToken));
27
27
  client_defined = true;
28
28
  }
29
- if (insightHubToken) {
30
- const insightHubClient = new InsightHubClient(insightHubToken, process.env.INSIGHT_HUB_PROJECT_API_KEY, process.env.INSIGHT_HUB_ENDPOINT);
31
- await insightHubClient.initialize();
32
- server.addClient(insightHubClient);
29
+ if (bugsnagToken) {
30
+ const bugsnagClient = new BugsnagClient(bugsnagToken, process.env.BUGSNAG_PROJECT_API_KEY, process.env.BUGSNAG_ENDPOINT);
31
+ await bugsnagClient.initialize();
32
+ server.addClient(bugsnagClient);
33
33
  client_defined = true;
34
34
  }
35
35
  if (apiHubToken) {
@@ -50,7 +50,7 @@ async function main() {
50
50
  }
51
51
  }
52
52
  if (!client_defined) {
53
- console.error("Please set one of REFLECT_API_TOKEN, INSIGHT_HUB_AUTH_TOKEN, API_HUB_API_KEY or PACT_BROKER_BASE_URL / (and relevant Pact auth) environment variables");
53
+ console.error("Please set one of REFLECT_API_TOKEN, BUGSNAG_AUTH_TOKEN, API_HUB_API_KEY or PACT_BROKER_BASE_URL / (and relevant Pact auth) environment variables");
54
54
  process.exit(1);
55
55
  }
56
56
  const transport = new StdioServerTransport();
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { addOpenAPISpecToSchema } from "./utils.js";
2
3
  // Type definitions for PactFlow AI API
3
4
  export const GenerationLanguages = [
4
5
  "javascript",
@@ -53,12 +54,11 @@ export const OpenAPISchema = z
53
54
  .describe("OpenAPI components section (schemas, responses, etc.)"),
54
55
  })
55
56
  .passthrough()
56
- .describe("The complete OpenAPI document describing the API")
57
+ .describe("The complete OpenAPI document describing the API.")
57
58
  .refine((data) => data.openapi || data.swagger, {
58
59
  message: "Either 'openapi' (for v3+) or 'swagger' (for v2) must be provided",
59
60
  path: ["openapi"],
60
- })
61
- .optional();
61
+ });
62
62
  export const EndpointMatcherSchema = z
63
63
  .object({
64
64
  path: z
@@ -80,12 +80,31 @@ export const EndpointMatcherSchema = z
80
80
  })
81
81
  .required()
82
82
  .describe("REQUIRED: Matcher to specify which endpoints from the OpenAPI document to generate tests for. At least one matcher field must be provided");
83
+ export const RemoteOpenAPIDocumentSchema = z
84
+ .object({
85
+ authToken: z
86
+ .string()
87
+ .describe("Auth Bearer Token if the OpenAPI spec requires authentication.")
88
+ .optional(),
89
+ authScheme: z
90
+ .string()
91
+ .describe("Authentication scheme (e.g., 'Bearer', 'Basic'). Default scheme passed should be Bearer if authToken is specified and this field is not set.")
92
+ .default("Bearer")
93
+ .optional(),
94
+ url: z
95
+ .string()
96
+ .url("Must be a valid openapi url")
97
+ .describe("URL of the remote OpenAPI document.")
98
+ .optional(),
99
+ })
100
+ .describe("Use this schema to fetch openapi documents present over a url.");
83
101
  export const OpenAPIWithMatcherSchema = z
84
102
  .object({
85
- document: OpenAPISchema,
103
+ document: OpenAPISchema.describe("The OpenAPI document describing the API being tested. if document is not provided, don't add the field if remoteOpenAPIDocument is provided.").optional(),
86
104
  matcher: EndpointMatcherSchema,
105
+ remoteDocument: RemoteOpenAPIDocumentSchema.optional().describe("The remote OpenAPI document to use for the review/generation in case openapi document is not provided. If provided do not include the document field under openapi."),
87
106
  })
88
- .describe("If provided, the OpenAPI document which describes the API being tested and is accompanied by a matcher which will be used to identify the interactions in the OpenAPI document which are relevant to the Pact refinement process.");
107
+ .describe("If provided, the OpenAPI document which describes the API being tested and is accompanied by a matcher which will be used to identify the interactions in the OpenAPI document which are relevant to the Pact refinement process.").transform(addOpenAPISpecToSchema);
89
108
  export const RefineInputSchema = z.object({
90
109
  pactTests: FileInputSchema.describe("Primary pact tests that needs to be refined."),
91
110
  code: z
@@ -1 +1,6 @@
1
- export {};
1
+ import { z } from "zod";
2
+ export const CanIDeploySchema = z.object({
3
+ pacticipant: z.string(),
4
+ version: z.string(),
5
+ environment: z.string(),
6
+ });
@@ -11,6 +11,7 @@
11
11
  */
12
12
  import { z } from "zod";
13
13
  import { GenerationInputSchema, RefineInputSchema } from "./ai.js";
14
+ import { CanIDeploySchema } from "./base.js";
14
15
  export const TOOLS = [
15
16
  {
16
17
  title: "Generate Pact Tests",
@@ -42,5 +43,13 @@ export const TOOLS = [
42
43
  ],
43
44
  handler: "getProviderStates",
44
45
  clients: ["pactflow", "pact_broker"]
46
+ },
47
+ {
48
+ title: "Can I Deploy",
49
+ summary: "Performs a comprehensive compatibility check to determine whether a specific version of a service (pacticipant) can be safely deployed into a given environment. It analyzes the complete contract matrix of consumer-provider relationships to confirm that all required integrations are verified and compatible.",
50
+ purpose: "To serve as a deployment safety check within the PactBroker and PactFlow ecosystem, leveraging contract testing results to validate whether a specific service / pacticipant version is compatible with all integrated services. This feature prevents unsafe releases, reduces integration risks, and enables teams to confidently automate deployments across environments with a clear, auditable record of verification results.",
51
+ zodSchema: CanIDeploySchema,
52
+ handler: "canIDeploy",
53
+ clients: ["pactflow", "pact_broker"]
45
54
  }
46
55
  ];