@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 +20 -20
- package/dist/{insight-hub → bugsnag}/client/api/CurrentUser.js +2 -1
- package/dist/{insight-hub → bugsnag}/client/api/Error.js +37 -4
- package/dist/{insight-hub → bugsnag}/client/api/Project.js +2 -1
- package/dist/{insight-hub → bugsnag}/client/api/base.js +18 -1
- package/dist/{insight-hub → bugsnag}/client/api/filters.js +2 -2
- package/dist/{insight-hub → bugsnag}/client.js +87 -19
- package/dist/common/info.js +1 -1
- package/dist/common/server.js +8 -5
- package/dist/index.js +9 -9
- package/dist/pactflow/client/ai.js +24 -5
- package/dist/pactflow/client/base.js +6 -1
- package/dist/pactflow/client/tools.js +9 -0
- package/dist/pactflow/client/utils.js +70 -0
- package/dist/pactflow/client.js +58 -10
- package/package.json +6 -3
- package/dist/package.json +0 -60
- package/dist/tests/unit/common/server.test.js +0 -319
- package/dist/tests/unit/insight-hub/api-utilities.test.js +0 -31
- package/dist/tests/unit/insight-hub/client.test.js +0 -852
- package/dist/tests/unit/insight-hub/filters.test.js +0 -93
- package/dist/tests/unit/pactflow/ai.test.js +0 -21
- package/dist/tests/unit/pactflow/client.test.js +0 -67
- package/dist/tests/unit/pactflow/tools.test.js +0 -34
- package/dist/vitest.config.js +0 -57
- /package/dist/{insight-hub → bugsnag}/client/api/index.js +0 -0
- /package/dist/{insight-hub → bugsnag}/client/configuration.js +0 -0
- /package/dist/{insight-hub → bugsnag}/client/index.js +0 -0
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 [
|
|
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
|
-
- [
|
|
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 (
|
|
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
|
|
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
|
-
"
|
|
70
|
-
"
|
|
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": "
|
|
82
|
+
"id": "bugsnag_auth_token",
|
|
83
83
|
"type": "promptString",
|
|
84
|
-
"description": "
|
|
84
|
+
"description": "BugSnag Auth Token - leave blank to disable BugSnag tools",
|
|
85
85
|
"password": true
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
|
-
"id": "
|
|
88
|
+
"id": "bugsnag_project_api_key",
|
|
89
89
|
"type": "promptString",
|
|
90
|
-
"description": "
|
|
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
|
-
"
|
|
149
|
-
"
|
|
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
|
-
"
|
|
206
|
-
"
|
|
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": "
|
|
218
|
+
"id": "bugsnag_auth_token",
|
|
219
219
|
"type": "promptString",
|
|
220
|
-
"description": "
|
|
220
|
+
"description": "BugSnag Auth Token - leave blank to disable BugSnag tools",
|
|
221
221
|
"password": true
|
|
222
222
|
},
|
|
223
223
|
{
|
|
224
|
-
"id": "
|
|
224
|
+
"id": "bugsnag_project_api_key",
|
|
225
225
|
"type": "promptString",
|
|
226
|
-
"description": "
|
|
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
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
|
2
|
+
* Filters utility for BugSnag API
|
|
3
3
|
*
|
|
4
4
|
* This file provides utility functions for creating filter URL parameters
|
|
5
|
-
* based on the
|
|
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 = "
|
|
9
|
+
const HUB_DOMAIN = "bugsnag.smartbear.com";
|
|
10
10
|
const cacheKeys = {
|
|
11
|
-
ORG: "
|
|
12
|
-
PROJECTS: "
|
|
13
|
-
CURRENT_PROJECT: "
|
|
14
|
-
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
|
|
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 = "
|
|
37
|
-
prefix = "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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 -
|
|
643
|
+
"Only use valid operations - BugSnag may reject invalid values"
|
|
576
644
|
],
|
|
577
645
|
readOnly: false,
|
|
578
646
|
idempotent: false,
|
package/dist/common/info.js
CHANGED
package/dist/common/server.js
CHANGED
|
@@ -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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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)})
|
|
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 {
|
|
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
|
|
11
|
-
const McpServerBugsnagAPIKey = process.env.
|
|
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
|
|
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 (
|
|
30
|
-
const
|
|
31
|
-
await
|
|
32
|
-
server.addClient(
|
|
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,
|
|
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
|
|
@@ -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
|
];
|