@smartbear/mcp 0.12.0 → 0.12.1

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.
@@ -1,5 +1,6 @@
1
+ import { toUrlSearchParams } from "../filters.js";
1
2
  import { ErrorsApiFetchParamCreator, } from "./api.js";
2
- import { BaseAPI, getQueryParams } from "./base.js";
3
+ import { BaseAPI } from "./base.js";
3
4
  export class ErrorAPI extends BaseAPI {
4
5
  static filterFields = ["url", "project_url", "events_url"];
5
6
  /**
@@ -14,9 +15,27 @@ export class ErrorAPI extends BaseAPI {
14
15
  * Get the latest Event in a Project, with optional filters
15
16
  * GET /projects/{project_id}/events
16
17
  */
17
- async listEventsOnProject(projectId, base, sort, direction, perPage, filters, fullReports) {
18
- const localVarFetchArgs = ErrorsApiFetchParamCreator(this.configuration).listEventsOnProject(projectId, base ?? undefined, sort, direction, perPage, undefined, fullReports, { query: filters });
19
- return await this.requestArray(localVarFetchArgs.url, localVarFetchArgs.options, false);
18
+ async listEventsOnProject(projectId, base, sort, direction, perPage, filters, fullReports, nextUrl) {
19
+ if (nextUrl) {
20
+ // Don't allow override of these params when using nextUrl
21
+ direction = undefined;
22
+ sort = undefined;
23
+ base = undefined;
24
+ }
25
+ const localVarFetchArgs = ErrorsApiFetchParamCreator(this.configuration).listEventsOnProject(projectId, base ?? undefined, sort, direction, perPage, undefined, // Filters are encoded separately below
26
+ fullReports, undefined);
27
+ const url = new URL(nextUrl ?? localVarFetchArgs.url, this.configuration.basePath);
28
+ if (perPage) {
29
+ // Allow override of per page, even with nextUrl
30
+ url.searchParams.set("per_page", perPage.toString());
31
+ }
32
+ if (!nextUrl && filters) {
33
+ // Apply our own encoding of filters
34
+ toUrlSearchParams(filters).forEach((value, key) => {
35
+ url.searchParams.append(key, value);
36
+ });
37
+ }
38
+ return await this.requestArray(url.toString(), localVarFetchArgs.options, false);
20
39
  }
21
40
  /**
22
41
  * View an Event by ID
@@ -31,19 +50,26 @@ export class ErrorAPI extends BaseAPI {
31
50
  * GET /projects/{project_id}/errors
32
51
  */
33
52
  async listProjectErrors(projectId, base, sort, direction, perPage, filters, nextUrl) {
34
- const options = getQueryParams(nextUrl, nextUrl ? undefined : filters);
35
53
  if (nextUrl) {
36
- if (perPage) {
37
- // Next links need to be used as-is to ensure results are consistent, so only the page size can be modified
38
- // the others will get overridden
39
- options.query.per_page = perPage.toString();
40
- }
54
+ // Don't allow override of these params when using nextUrl
41
55
  direction = undefined;
42
56
  sort = undefined;
43
57
  base = undefined;
44
58
  }
45
- const localVarFetchArgs = ErrorsApiFetchParamCreator(this.configuration).listProjectErrors(projectId, base ?? undefined, sort, direction, perPage, undefined, undefined, options);
46
- return await this.requestArray(localVarFetchArgs.url, localVarFetchArgs.options, false);
59
+ const localVarFetchArgs = ErrorsApiFetchParamCreator(this.configuration).listProjectErrors(projectId, base ?? undefined, sort, direction, undefined, undefined, // Filters are encoded separately below
60
+ undefined, undefined);
61
+ const url = new URL(nextUrl ?? localVarFetchArgs.url, this.configuration.basePath);
62
+ if (perPage) {
63
+ // Allow override of per page, even with nextUrl
64
+ url.searchParams.set("per_page", perPage.toString());
65
+ }
66
+ if (!nextUrl && filters) {
67
+ // Apply our own encoding of filters
68
+ toUrlSearchParams(filters).forEach((value, key) => {
69
+ url.searchParams.append(key, value);
70
+ });
71
+ }
72
+ return await this.requestArray(url.toString(), localVarFetchArgs.options, false);
47
73
  }
48
74
  /**
49
75
  * Update an Error on a Project
@@ -58,7 +84,19 @@ export class ErrorAPI extends BaseAPI {
58
84
  * GET /projects/{project_id}/errors/{error_id}/pivots
59
85
  */
60
86
  async getPivotValuesOnAnError(projectId, errorId, filters, summarySize, pivots, perPage) {
61
- const localVarFetchArgs = ErrorsApiFetchParamCreator(this.configuration).listPivotsOnAnError(projectId, errorId, undefined, summarySize, pivots, perPage, { query: filters });
62
- return await this.requestArray(localVarFetchArgs.url, localVarFetchArgs.options, false);
87
+ const localVarFetchArgs = ErrorsApiFetchParamCreator(this.configuration).listPivotsOnAnError(projectId, errorId, undefined, // filters are encoded separately below
88
+ summarySize, pivots, undefined);
89
+ const url = new URL(localVarFetchArgs.url, this.configuration.basePath);
90
+ if (perPage) {
91
+ // Allow override of per page, even with nextUrl
92
+ url.searchParams.set("per_page", perPage.toString());
93
+ }
94
+ if (filters) {
95
+ // Apply our own encoding of filters
96
+ toUrlSearchParams(filters).forEach((value, key) => {
97
+ url.searchParams.append(key, value);
98
+ });
99
+ }
100
+ return await this.requestArray(url.toString(), localVarFetchArgs.options, false);
63
101
  }
64
102
  }
@@ -68,21 +68,25 @@ export class ProjectAPI extends BaseAPI {
68
68
  * Lists releases for a specific project.
69
69
  * GET /projects/{project_id}/release_groups
70
70
  * @param projectId The ID of the project.
71
- * @param opts Options for listing releases, including filtering by release stage and visibility.
71
+ * @param releaseStageName The name of the release stage to filter by.
72
+ * @param topOnly Whether to only include top-level releases.
73
+ * @param visibleOnly Whether to only include visible releases.
74
+ * @param perPage The number of results per page.
75
+ * @param nextUrl Optional URL for next page (overrides other pagination params).
72
76
  * @returns A promise that resolves to an array of `ReleaseSummaryResponse` objects.
73
77
  */
74
78
  async listProjectReleaseGroups(projectId, releaseStageName, topOnly, visibleOnly, perPage, nextUrl) {
75
- const options = getQueryParams(nextUrl);
76
- // Next links need to be used as-is to ensure results are consistent, so only the page size can be modified
77
- // the others will get overridden
78
79
  if (nextUrl) {
79
- options.query.per_page = perPage ? perPage.toString() : undefined;
80
80
  topOnly = undefined;
81
81
  visibleOnly = undefined;
82
82
  }
83
- const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).listProjectReleaseGroups(projectId, releaseStageName, topOnly, visibleOnly, perPage, undefined, // pageToken passed in through options
84
- options);
85
- return await this.requestArray(localVarFetchArgs.url, localVarFetchArgs.options, false, // Paginate results
83
+ const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).listProjectReleaseGroups(projectId, releaseStageName, topOnly, visibleOnly, perPage, undefined, undefined);
84
+ const url = new URL(nextUrl ?? localVarFetchArgs.url, this.configuration.basePath);
85
+ if (perPage) {
86
+ // Allow override of per page, even with nextUrl
87
+ url.searchParams.set("per_page", perPage.toString());
88
+ }
89
+ return await this.requestArray(url.toString(), localVarFetchArgs.options, false, // Paginate results
86
90
  ProjectAPI.releaseFields);
87
91
  }
88
92
  /**
@@ -122,17 +126,25 @@ export class ProjectAPI extends BaseAPI {
122
126
  * @returns A promise that resolves to an array of span groups.
123
127
  */
124
128
  async listProjectSpanGroups(projectId, sort, direction, perPage, offset, filters, starredOnly, nextUrl) {
125
- const options = getQueryParams(nextUrl);
126
129
  if (nextUrl) {
127
- options.query.per_page = perPage ? perPage.toString() : undefined;
130
+ sort = undefined;
131
+ direction = undefined;
132
+ offset = undefined;
128
133
  }
129
- // Serialize filters if provided
130
- if (filters) {
131
- Object.assign(options.query, toUrlSearchParams(filters));
134
+ const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).listProjectSpanGroups(projectId, sort, direction, perPage, offset, undefined, // Filters are encoded separately below
135
+ starredOnly, undefined);
136
+ const url = new URL(nextUrl ?? localVarFetchArgs.url, this.configuration.basePath);
137
+ if (perPage) {
138
+ // Allow override of per page, even with nextUrl
139
+ url.searchParams.set("per_page", perPage.toString());
132
140
  }
133
- const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).listProjectSpanGroups(projectId, sort, direction, perPage, offset, undefined, // Don't pass filters to API - already serialized in options
134
- starredOnly, options);
135
- return await this.requestArray(localVarFetchArgs.url, localVarFetchArgs.options, false);
141
+ if (!nextUrl && filters) {
142
+ // Apply our own encoding of filters
143
+ toUrlSearchParams(filters).forEach((value, key) => {
144
+ url.searchParams.append(key, value);
145
+ });
146
+ }
147
+ return await this.requestArray(url.toString(), localVarFetchArgs.options, false);
136
148
  }
137
149
  /**
138
150
  * Get detailed information about a specific span group.
@@ -143,12 +155,15 @@ export class ProjectAPI extends BaseAPI {
143
155
  * @returns A promise that resolves to the span group.
144
156
  */
145
157
  async getProjectSpanGroup(projectId, id, filters) {
146
- const options = {};
158
+ const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).getProjectSpanGroup(projectId, id, undefined, undefined);
159
+ const url = new URL(localVarFetchArgs.url, this.configuration.basePath);
147
160
  if (filters) {
148
- options.query = toUrlSearchParams(filters);
161
+ // Apply our own encoding of filters
162
+ toUrlSearchParams(filters).forEach((value, key) => {
163
+ url.searchParams.append(key, value);
164
+ });
149
165
  }
150
- const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).getProjectSpanGroup(projectId, id, undefined, options);
151
- return await this.requestObject(localVarFetchArgs.url, localVarFetchArgs.options);
166
+ return await this.requestObject(url.toString(), localVarFetchArgs.options);
152
167
  }
153
168
  /**
154
169
  * Get time-series performance metrics for a span group.
@@ -159,12 +174,15 @@ export class ProjectAPI extends BaseAPI {
159
174
  * @returns A promise that resolves to the timeline data.
160
175
  */
161
176
  async getProjectSpanGroupTimeline(projectId, id, filters) {
162
- const options = {};
177
+ const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).getProjectSpanGroupTimeline(projectId, id, undefined, undefined);
178
+ const url = new URL(localVarFetchArgs.url, this.configuration.basePath);
163
179
  if (filters) {
164
- options.query = toUrlSearchParams(filters);
180
+ // Apply our own encoding of filters
181
+ toUrlSearchParams(filters).forEach((value, key) => {
182
+ url.searchParams.append(key, value);
183
+ });
165
184
  }
166
- const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).getProjectSpanGroupTimeline(projectId, id, undefined, options);
167
- return await this.requestObject(localVarFetchArgs.url, localVarFetchArgs.options);
185
+ return await this.requestObject(url.toString(), localVarFetchArgs.options);
168
186
  }
169
187
  /**
170
188
  * Get distribution histogram of span durations for a span group.
@@ -179,8 +197,15 @@ export class ProjectAPI extends BaseAPI {
179
197
  if (filters) {
180
198
  options.query = toUrlSearchParams(filters);
181
199
  }
182
- const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).getProjectSpanGroupDistribution(projectId, id, undefined, options);
183
- return await this.requestObject(localVarFetchArgs.url, localVarFetchArgs.options);
200
+ const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).getProjectSpanGroupDistribution(projectId, id, undefined, undefined);
201
+ const url = new URL(localVarFetchArgs.url, this.configuration.basePath);
202
+ if (filters) {
203
+ // Apply our own encoding of filters
204
+ toUrlSearchParams(filters).forEach((value, key) => {
205
+ url.searchParams.append(key, value);
206
+ });
207
+ }
208
+ return await this.requestObject(url.toString(), localVarFetchArgs.options);
184
209
  }
185
210
  /**
186
211
  * List individual spans for a specific span group with filtering and sorting.
@@ -195,15 +220,23 @@ export class ProjectAPI extends BaseAPI {
195
220
  * @returns A promise that resolves to an array of spans.
196
221
  */
197
222
  async listSpansBySpanGroupId(projectId, id, filters, sort, direction, perPage, nextUrl) {
198
- const options = getQueryParams(nextUrl);
199
223
  if (nextUrl) {
200
- options.query.per_page = perPage ? perPage.toString() : undefined;
224
+ sort = undefined;
225
+ direction = undefined;
201
226
  }
202
- if (filters) {
203
- Object.assign(options.query, toUrlSearchParams(filters));
227
+ const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).listSpansBySpanGroupId(projectId, id, undefined, sort, direction, perPage, undefined);
228
+ const url = new URL(nextUrl ?? localVarFetchArgs.url, this.configuration.basePath);
229
+ if (perPage) {
230
+ // Allow override of per page, even with nextUrl
231
+ url.searchParams.set("per_page", perPage.toString());
204
232
  }
205
- const localVarFetchArgs = ProjectsApiFetchParamCreator(this.configuration).listSpansBySpanGroupId(projectId, id, undefined, sort, direction, perPage, options);
206
- return await this.requestArray(localVarFetchArgs.url, localVarFetchArgs.options, false);
233
+ if (!nextUrl && filters) {
234
+ // Apply our own encoding of filters
235
+ toUrlSearchParams(filters).forEach((value, key) => {
236
+ url.searchParams.append(key, value);
237
+ });
238
+ }
239
+ return await this.requestArray(url.toString(), localVarFetchArgs.options, false);
207
240
  }
208
241
  /**
209
242
  * List all spans that belong to a specific trace.
@@ -234,6 +234,17 @@ export class BugsnagClient {
234
234
  const projectEvents = await Promise.all(projectIds.map((projectId) => this.errorsApi.viewEventById(projectId, eventId).catch((_e) => null)));
235
235
  return projectEvents.find((event) => event && !!event.body)?.body || null;
236
236
  }
237
+ async validateEventFields(project, fields) {
238
+ if (fields) {
239
+ const eventFields = await this.getProjectEventFields(project);
240
+ const validKeys = new Set(eventFields.map((f) => f.displayId));
241
+ for (const key of Object.keys(fields)) {
242
+ if (!validKeys.has(key)) {
243
+ throw new ToolError(`Invalid filter key: ${key}`);
244
+ }
245
+ }
246
+ }
247
+ }
237
248
  async getInputProject(projectId) {
238
249
  if (typeof projectId === "string") {
239
250
  const maybeProject = await this.getProject(projectId);
@@ -389,6 +400,7 @@ export class BugsnagClient {
389
400
  error: [{ type: "eq", value: params.errorId }],
390
401
  ...args.filters,
391
402
  };
403
+ await this.validateEventFields(project, filters);
392
404
  // Get the latest event for this error using the events endpoint with filters
393
405
  let latestEvent = null;
394
406
  try {
@@ -554,21 +566,13 @@ export class BugsnagClient {
554
566
  }, async (args, _extra) => {
555
567
  const params = listProjectErrorsInputSchema.parse(args);
556
568
  const project = await this.getInputProject(params.projectId);
557
- // Validate filter keys against cached event fields
558
- if (params.filters) {
559
- const eventFields = await this.getProjectEventFields(project);
560
- const validKeys = new Set(eventFields.map((f) => f.displayId));
561
- for (const key of Object.keys(params.filters)) {
562
- if (!validKeys.has(key)) {
563
- throw new ToolError(`Invalid filter key: ${key}`);
564
- }
565
- }
566
- }
567
569
  const filters = {
568
570
  "event.since": [{ type: "eq", value: "30d" }],
569
571
  "error.status": [{ type: "eq", value: "open" }],
570
572
  ...params.filters,
571
573
  };
574
+ // Validate filter keys against cached event fields
575
+ await this.validateEventFields(project, filters);
572
576
  const response = await this.errorsApi.listProjectErrors(project.id, null, params.sort, params.direction, params.perPage, filters, params.nextUrl);
573
577
  const result = {
574
578
  data: response.body,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartbear/mcp",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "MCP server for interacting SmartBear Products",
5
5
  "keywords": [
6
6
  "smartbear",