@smartbear/mcp 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bugsnag/client/api/Error.js +1 -1
- package/dist/bugsnag/client/api/Project.js +152 -0
- package/dist/bugsnag/client/api/api.js +130 -3
- package/dist/bugsnag/client/api/base.js +19 -0
- package/dist/bugsnag/client/api/index.js +1 -1
- package/dist/bugsnag/client.js +514 -11
- package/dist/bugsnag/input-schemas.js +14 -6
- package/dist/common/transport-stdio.js +5 -0
- package/dist/qmetry/client/auto-resolve.js +10 -0
- package/dist/qmetry/client/automation.js +171 -0
- package/dist/qmetry/client/handlers.js +9 -2
- package/dist/qmetry/client/project.js +87 -1
- package/dist/qmetry/client/testsuite.js +37 -1
- package/dist/qmetry/client/tools/automation-tools.js +290 -0
- package/dist/qmetry/client/tools/index.js +3 -0
- package/dist/qmetry/client/tools/issue-tools.js +1 -1
- package/dist/qmetry/client/tools/project-tools.js +311 -1
- package/dist/qmetry/client/tools/requirement-tools.js +1 -1
- package/dist/qmetry/client/tools/testcase-tools.js +311 -39
- package/dist/qmetry/client/tools/testsuite-tools.js +337 -23
- package/dist/qmetry/config/constants.js +6 -0
- package/dist/qmetry/config/rest-endpoints.js +8 -0
- package/dist/qmetry/types/automation.js +8 -0
- package/dist/qmetry/types/common.js +299 -4
- package/dist/qmetry/types/issues.js +5 -0
- package/dist/qmetry/types/project.js +13 -0
- package/dist/qmetry/types/requirements.js +5 -0
- package/dist/qmetry/types/testcase.js +5 -0
- package/dist/qmetry/types/testsuite.js +9 -0
- package/dist/swagger/client/api.js +93 -36
- package/dist/swagger/client/configuration.js +3 -1
- package/dist/swagger/client/portal-types.js +7 -6
- package/dist/swagger/client/registry-types.js +26 -0
- package/dist/swagger/client/tools.js +15 -16
- package/dist/swagger/client.js +6 -6
- package/dist/tests/unit/bugsnag/utils/factories.js +86 -0
- package/dist/zephyr/client.js +4 -0
- package/dist/zephyr/tool/environment/get-environments.js +68 -0
- package/dist/zephyr/tool/test-execution/get-test-executions.js +45 -0
- package/package.json +3 -2
package/dist/bugsnag/client.js
CHANGED
|
@@ -10,7 +10,8 @@ const HUB_DOMAIN = "bugsnag.smartbear.com";
|
|
|
10
10
|
const cacheKeys = {
|
|
11
11
|
ORG: "bugsnag_org",
|
|
12
12
|
PROJECTS: "bugsnag_projects",
|
|
13
|
-
|
|
13
|
+
PROJECT_EVENT_FIELDS: "bugsnag_project_event_fields",
|
|
14
|
+
PROJECT_TRACE_FIELDS: "bugsnag_project_trace_fields",
|
|
14
15
|
CURRENT_PROJECT: "bugsnag_current_project",
|
|
15
16
|
};
|
|
16
17
|
// Exclude certain event fields from the project event filters to improve agent usage
|
|
@@ -101,7 +102,7 @@ export class BugsnagClient {
|
|
|
101
102
|
console.error("An error occurred while fetching project information", error);
|
|
102
103
|
}
|
|
103
104
|
if (currentProject) {
|
|
104
|
-
await this.
|
|
105
|
+
await this.getProjectEventFields(currentProject);
|
|
105
106
|
}
|
|
106
107
|
else {
|
|
107
108
|
// Clear the project API key to allow tools to work across all projects
|
|
@@ -201,8 +202,8 @@ export class BugsnagClient {
|
|
|
201
202
|
}
|
|
202
203
|
return project;
|
|
203
204
|
}
|
|
204
|
-
async
|
|
205
|
-
const projectFiltersCache = this.cache?.get(cacheKeys.
|
|
205
|
+
async getProjectEventFields(project) {
|
|
206
|
+
const projectFiltersCache = this.cache?.get(cacheKeys.PROJECT_EVENT_FIELDS) || {};
|
|
206
207
|
if (!projectFiltersCache[project.id]) {
|
|
207
208
|
let filtersResponse = (await this.projectApi.listProjectEventFields(project.id)).body;
|
|
208
209
|
if (!filtersResponse || filtersResponse.length === 0) {
|
|
@@ -210,7 +211,19 @@ export class BugsnagClient {
|
|
|
210
211
|
}
|
|
211
212
|
filtersResponse = filtersResponse.filter((field) => field.displayId && !EXCLUDED_EVENT_FIELDS.has(field.displayId));
|
|
212
213
|
projectFiltersCache[project.id] = filtersResponse;
|
|
213
|
-
this.cache?.set(cacheKeys.
|
|
214
|
+
this.cache?.set(cacheKeys.PROJECT_EVENT_FIELDS, projectFiltersCache);
|
|
215
|
+
}
|
|
216
|
+
return projectFiltersCache[project.id];
|
|
217
|
+
}
|
|
218
|
+
async getProjectTraceFields(project) {
|
|
219
|
+
const projectFiltersCache = this.cache?.get(cacheKeys.PROJECT_TRACE_FIELDS) || {};
|
|
220
|
+
if (!projectFiltersCache[project.id]) {
|
|
221
|
+
const filtersResponse = (await this.projectApi.listProjectTraceFields(project.id)).body;
|
|
222
|
+
if (!filtersResponse || filtersResponse.length === 0) {
|
|
223
|
+
throw new ToolError(`No trace fields found for project ${project.name}.`);
|
|
224
|
+
}
|
|
225
|
+
projectFiltersCache[project.id] = filtersResponse;
|
|
226
|
+
this.cache?.set(cacheKeys.PROJECT_TRACE_FIELDS, projectFiltersCache);
|
|
214
227
|
}
|
|
215
228
|
return projectFiltersCache[project.id];
|
|
216
229
|
}
|
|
@@ -361,6 +374,7 @@ export class BugsnagClient {
|
|
|
361
374
|
hints: [
|
|
362
375
|
"Error IDs can be found using the List Project Errors tool",
|
|
363
376
|
"Use this after filtering errors to get detailed information about specific errors",
|
|
377
|
+
"Use Get Event Details tool if you need detailed information about a specific event (occurrence) rather than the aggregated error",
|
|
364
378
|
"If you used a filter to get this error, you can pass the same filters here to restrict the results or apply further filters",
|
|
365
379
|
"The URL provided in the response points should be shown to the user in all cases as it allows them to view the error in the dashboard and perform further analysis",
|
|
366
380
|
],
|
|
@@ -381,6 +395,7 @@ export class BugsnagClient {
|
|
|
381
395
|
const latestEvents = (await this.errorsApi.listEventsOnProject(project.id, null, "timestamp", "desc", 1, filters, true)).body;
|
|
382
396
|
if (latestEvents && latestEvents.length > 0) {
|
|
383
397
|
latestEvent = latestEvents[0];
|
|
398
|
+
latestEvent.threads = undefined; // Remove threads to reduce payload size
|
|
384
399
|
}
|
|
385
400
|
}
|
|
386
401
|
catch (e) {
|
|
@@ -397,13 +412,42 @@ export class BugsnagClient {
|
|
|
397
412
|
content: [{ type: "text", text: JSON.stringify(content) }],
|
|
398
413
|
};
|
|
399
414
|
});
|
|
400
|
-
const
|
|
415
|
+
const getEventInputSchema = z.object({
|
|
416
|
+
projectId: toolInputParameters.projectId,
|
|
417
|
+
eventId: toolInputParameters.eventId,
|
|
418
|
+
});
|
|
419
|
+
register({
|
|
420
|
+
title: "Get Event",
|
|
421
|
+
summary: "Get detailed information about a specific event",
|
|
422
|
+
purpose: "Retrieve event details directly from its ID",
|
|
423
|
+
useCases: [
|
|
424
|
+
"Get the full details of an event, including any thread stack traces",
|
|
425
|
+
],
|
|
426
|
+
inputSchema: getEventInputSchema,
|
|
427
|
+
examples: [
|
|
428
|
+
{
|
|
429
|
+
description: "Get event details of an event",
|
|
430
|
+
parameters: {
|
|
431
|
+
eventId: "6863e2af012caf1d5c320000",
|
|
432
|
+
},
|
|
433
|
+
expectedOutput: "JSON object with complete event details including stack trace (error trace and other threads, if present), metadata, and context",
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
}, async (args, _extra) => {
|
|
437
|
+
const params = getEventInputSchema.parse(args);
|
|
438
|
+
const project = await this.getInputProject(params.projectId);
|
|
439
|
+
const response = await this.getEvent(params.eventId, project.id);
|
|
440
|
+
return {
|
|
441
|
+
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
442
|
+
};
|
|
443
|
+
});
|
|
444
|
+
const getEventDetailsFromDashboardUrlInputSchema = z.object({
|
|
401
445
|
link: z
|
|
402
446
|
.string()
|
|
403
447
|
.describe("Full URL to the event details page in the BugSnag dashboard (web interface), containing project slug and event_id parameter."),
|
|
404
448
|
});
|
|
405
449
|
register({
|
|
406
|
-
title: "Get Event Details",
|
|
450
|
+
title: "Get Event Details From Dashboard URL",
|
|
407
451
|
summary: "Get detailed information about a specific event using its dashboard URL",
|
|
408
452
|
purpose: "Retrieve event details directly from a dashboard URL for quick debugging",
|
|
409
453
|
useCases: [
|
|
@@ -411,7 +455,7 @@ export class BugsnagClient {
|
|
|
411
455
|
"Extract event information from shared links or browser URLs",
|
|
412
456
|
"Quick lookup of event details without needing separate project and event IDs",
|
|
413
457
|
],
|
|
414
|
-
inputSchema:
|
|
458
|
+
inputSchema: getEventDetailsFromDashboardUrlInputSchema,
|
|
415
459
|
examples: [
|
|
416
460
|
{
|
|
417
461
|
description: "Get event details from a dashboard URL",
|
|
@@ -426,7 +470,7 @@ export class BugsnagClient {
|
|
|
426
470
|
"This is useful when users share BugSnag dashboard URLs and you need to extract the event data",
|
|
427
471
|
],
|
|
428
472
|
}, async (args, _extra) => {
|
|
429
|
-
const params =
|
|
473
|
+
const params = getEventDetailsFromDashboardUrlInputSchema.parse(args);
|
|
430
474
|
const url = new URL(params.link);
|
|
431
475
|
const eventId = url.searchParams.get("event_id");
|
|
432
476
|
const projectSlug = url.pathname.split("/")[2];
|
|
@@ -512,7 +556,7 @@ export class BugsnagClient {
|
|
|
512
556
|
const project = await this.getInputProject(params.projectId);
|
|
513
557
|
// Validate filter keys against cached event fields
|
|
514
558
|
if (params.filters) {
|
|
515
|
-
const eventFields = await this.
|
|
559
|
+
const eventFields = await this.getProjectEventFields(project);
|
|
516
560
|
const validKeys = new Set(eventFields.map((f) => f.displayId));
|
|
517
561
|
for (const key of Object.keys(params.filters)) {
|
|
518
562
|
if (!validKeys.has(key)) {
|
|
@@ -562,7 +606,7 @@ export class BugsnagClient {
|
|
|
562
606
|
],
|
|
563
607
|
}, async (args, _extra) => {
|
|
564
608
|
const params = listProjectEventFiltersInputSchema.parse(args);
|
|
565
|
-
const eventFilters = await this.
|
|
609
|
+
const eventFilters = await this.getProjectEventFields(await this.getInputProject(params.projectId));
|
|
566
610
|
return {
|
|
567
611
|
content: [{ type: "text", text: JSON.stringify(eventFilters) }],
|
|
568
612
|
};
|
|
@@ -800,6 +844,465 @@ export class BugsnagClient {
|
|
|
800
844
|
content: [{ type: "text", text: JSON.stringify(build) }],
|
|
801
845
|
};
|
|
802
846
|
});
|
|
847
|
+
// ============================================================
|
|
848
|
+
// Performance Monitoring Tools
|
|
849
|
+
// ============================================================
|
|
850
|
+
const listSpanGroupsInputSchema = z.object({
|
|
851
|
+
projectId: toolInputParameters.projectId,
|
|
852
|
+
sort: z
|
|
853
|
+
.enum([
|
|
854
|
+
"total_spans",
|
|
855
|
+
"last_seen",
|
|
856
|
+
"name",
|
|
857
|
+
"display_name",
|
|
858
|
+
"network_http_method",
|
|
859
|
+
"rendering_slow_frame_span_percentage",
|
|
860
|
+
"rendering_frozen_frame_span_percentage",
|
|
861
|
+
"duration_p50",
|
|
862
|
+
"duration_p75",
|
|
863
|
+
"duration_p90",
|
|
864
|
+
"duration_p95",
|
|
865
|
+
"duration_p99",
|
|
866
|
+
"system_metrics_cpu_total_mean_p50",
|
|
867
|
+
"system_metrics_cpu_total_mean_p75",
|
|
868
|
+
"system_metrics_cpu_total_mean_p90",
|
|
869
|
+
"system_metrics_cpu_total_mean_p95",
|
|
870
|
+
"system_metrics_cpu_total_mean_p99",
|
|
871
|
+
"system_metrics_memory_device_mean_p50",
|
|
872
|
+
"system_metrics_memory_device_mean_p75",
|
|
873
|
+
"system_metrics_memory_device_mean_p90",
|
|
874
|
+
"system_metrics_memory_device_mean_p95",
|
|
875
|
+
"system_metrics_memory_device_mean_p99",
|
|
876
|
+
"rendering_metrics_fps_mean_p50",
|
|
877
|
+
"rendering_metrics_fps_mean_p75",
|
|
878
|
+
"rendering_metrics_fps_mean_p90",
|
|
879
|
+
"rendering_metrics_fps_mean_p95",
|
|
880
|
+
"rendering_metrics_fps_mean_p99",
|
|
881
|
+
"http_response_4xx_percentage",
|
|
882
|
+
"http_response_5xx_percentage",
|
|
883
|
+
])
|
|
884
|
+
.optional()
|
|
885
|
+
.describe("Field to sort by"),
|
|
886
|
+
direction: toolInputParameters.direction,
|
|
887
|
+
perPage: toolInputParameters.perPage,
|
|
888
|
+
starredOnly: z
|
|
889
|
+
.boolean()
|
|
890
|
+
.optional()
|
|
891
|
+
.describe("Show only starred span groups"),
|
|
892
|
+
nextUrl: toolInputParameters.nextUrl,
|
|
893
|
+
filters: toolInputParameters.performanceFilters,
|
|
894
|
+
});
|
|
895
|
+
register({
|
|
896
|
+
title: "List Span Groups",
|
|
897
|
+
summary: "List span groups (operations) tracked for performance monitoring",
|
|
898
|
+
purpose: "Discover and analyze different operations being monitored",
|
|
899
|
+
useCases: [
|
|
900
|
+
"View all operations being tracked for performance",
|
|
901
|
+
"Find slow operations by sorting by duration metrics",
|
|
902
|
+
"Filter to starred/important span groups",
|
|
903
|
+
],
|
|
904
|
+
inputSchema: listSpanGroupsInputSchema,
|
|
905
|
+
examples: [
|
|
906
|
+
{
|
|
907
|
+
description: "List slowest operations",
|
|
908
|
+
parameters: {
|
|
909
|
+
sort: "duration_p95",
|
|
910
|
+
direction: "desc",
|
|
911
|
+
perPage: 10,
|
|
912
|
+
},
|
|
913
|
+
expectedOutput: "Array of span groups sorted by 95th percentile duration",
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
description: "List starred span groups with filtering",
|
|
917
|
+
parameters: {
|
|
918
|
+
starredOnly: true,
|
|
919
|
+
filters: {
|
|
920
|
+
"span_group.category": [
|
|
921
|
+
{ type: "eq", value: "full_page_load" },
|
|
922
|
+
],
|
|
923
|
+
},
|
|
924
|
+
},
|
|
925
|
+
expectedOutput: "Array of starred span groups filtered by category",
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
hints: [
|
|
929
|
+
"Span groups represent different operation types (page loads, API calls, etc.)",
|
|
930
|
+
"Use sort by duration_p95 or duration_p99 to find the slowest operations",
|
|
931
|
+
"Star important span groups for quick access",
|
|
932
|
+
"Use nextUrl for pagination",
|
|
933
|
+
],
|
|
934
|
+
}, async (args, _extra) => {
|
|
935
|
+
const params = listSpanGroupsInputSchema.parse(args);
|
|
936
|
+
const project = await this.getInputProject(params.projectId);
|
|
937
|
+
const result = await this.projectApi.listProjectSpanGroups(project.id, params.sort, params.direction, params.perPage, undefined, params.filters, params.starredOnly, params.nextUrl);
|
|
938
|
+
return {
|
|
939
|
+
content: [
|
|
940
|
+
{
|
|
941
|
+
type: "text",
|
|
942
|
+
text: JSON.stringify({
|
|
943
|
+
data: result.body,
|
|
944
|
+
next_url: result.nextUrl,
|
|
945
|
+
count: result.body?.length,
|
|
946
|
+
}),
|
|
947
|
+
},
|
|
948
|
+
],
|
|
949
|
+
};
|
|
950
|
+
});
|
|
951
|
+
const getSpanGroupInputSchema = z.object({
|
|
952
|
+
projectId: toolInputParameters.projectId,
|
|
953
|
+
spanGroupId: toolInputParameters.spanGroupId,
|
|
954
|
+
filters: toolInputParameters.performanceFilters,
|
|
955
|
+
});
|
|
956
|
+
register({
|
|
957
|
+
title: "Get Span Group",
|
|
958
|
+
summary: "Get detailed performance metrics for a specific span group",
|
|
959
|
+
purpose: "Analyze performance characteristics of a specific operation",
|
|
960
|
+
useCases: [
|
|
961
|
+
"View detailed statistics (p50, p75, p90, p95, p99) for an operation",
|
|
962
|
+
"Check if performance targets are configured",
|
|
963
|
+
"Monitor span count to understand operation volume",
|
|
964
|
+
],
|
|
965
|
+
inputSchema: getSpanGroupInputSchema,
|
|
966
|
+
examples: [
|
|
967
|
+
{
|
|
968
|
+
description: "Get details for an API endpoint span group",
|
|
969
|
+
parameters: { spanGroupId: "[HttpClient]GET-api.example.com" },
|
|
970
|
+
expectedOutput: "Statistics, category, and performance target info",
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
description: "Get span group details with device filtering",
|
|
974
|
+
parameters: {
|
|
975
|
+
spanGroupId: "[HttpClient]GET-api.example.com",
|
|
976
|
+
filters: {
|
|
977
|
+
"device.browser_name": [{ type: "eq", value: "Chrome" }],
|
|
978
|
+
},
|
|
979
|
+
},
|
|
980
|
+
expectedOutput: "Statistics filtered for Chrome browser only",
|
|
981
|
+
},
|
|
982
|
+
],
|
|
983
|
+
hints: [
|
|
984
|
+
"Use List Span Groups first to discover available span group IDs",
|
|
985
|
+
"IDs are automatically URL-encoded - provide the raw ID",
|
|
986
|
+
"Statistics include p50, p75, p90, p95, p99 percentiles",
|
|
987
|
+
],
|
|
988
|
+
}, async (args, _extra) => {
|
|
989
|
+
const params = getSpanGroupInputSchema.parse(args);
|
|
990
|
+
const project = await this.getInputProject(params.projectId);
|
|
991
|
+
const spanGroupResults = await this.projectApi.getProjectSpanGroup(project.id, params.spanGroupId, params.filters);
|
|
992
|
+
const spanGroupTimelineResult = await this.projectApi.getProjectSpanGroupTimeline(project.id, params.spanGroupId, params.filters);
|
|
993
|
+
const spanGroupDistributionResult = await this.projectApi.getProjectSpanGroupDistribution(project.id, params.spanGroupId, params.filters);
|
|
994
|
+
const result = {
|
|
995
|
+
...spanGroupResults.body,
|
|
996
|
+
timeline: spanGroupTimelineResult.body,
|
|
997
|
+
distribution: spanGroupDistributionResult.body,
|
|
998
|
+
};
|
|
999
|
+
return {
|
|
1000
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
1001
|
+
};
|
|
1002
|
+
});
|
|
1003
|
+
const listSpansInputSchema = z.object({
|
|
1004
|
+
projectId: toolInputParameters.projectId,
|
|
1005
|
+
spanGroupId: toolInputParameters.spanGroupId,
|
|
1006
|
+
sort: z
|
|
1007
|
+
.enum([
|
|
1008
|
+
"duration",
|
|
1009
|
+
"timestamp",
|
|
1010
|
+
"full_page_load_lcp",
|
|
1011
|
+
"full_page_load_fid",
|
|
1012
|
+
"full_page_load_cls",
|
|
1013
|
+
"full_page_load_ttfb",
|
|
1014
|
+
"full_page_load_fcp",
|
|
1015
|
+
"rendering_slow_frame_percentage",
|
|
1016
|
+
"rendering_frozen_frame_percentage",
|
|
1017
|
+
"system_metrics_cpu_total_mean",
|
|
1018
|
+
"system_metrics_memory_device_mean",
|
|
1019
|
+
"rendering_metrics_fps_mean",
|
|
1020
|
+
"rendering_metrics_fps_minimum",
|
|
1021
|
+
"rendering_metrics_fps_maximum",
|
|
1022
|
+
"http_response_code",
|
|
1023
|
+
])
|
|
1024
|
+
.optional()
|
|
1025
|
+
.describe("Field to sort by"),
|
|
1026
|
+
direction: toolInputParameters.direction,
|
|
1027
|
+
perPage: toolInputParameters.perPage,
|
|
1028
|
+
nextUrl: toolInputParameters.nextUrl,
|
|
1029
|
+
filters: toolInputParameters.performanceFilters,
|
|
1030
|
+
});
|
|
1031
|
+
register({
|
|
1032
|
+
title: "List Spans",
|
|
1033
|
+
summary: "Get individual spans belonging to a span group",
|
|
1034
|
+
purpose: "Examine individual operation instances within a span group",
|
|
1035
|
+
useCases: [
|
|
1036
|
+
"Analyze individual slow operations",
|
|
1037
|
+
"Debug performance issues by examining specific traces",
|
|
1038
|
+
"Find patterns in operation attributes",
|
|
1039
|
+
],
|
|
1040
|
+
inputSchema: listSpansInputSchema,
|
|
1041
|
+
examples: [
|
|
1042
|
+
{
|
|
1043
|
+
description: "Get slowest spans for an operation",
|
|
1044
|
+
parameters: {
|
|
1045
|
+
spanGroupId: "[HttpClient]GET-api.example.com",
|
|
1046
|
+
sort: "duration",
|
|
1047
|
+
direction: "desc",
|
|
1048
|
+
perPage: 10,
|
|
1049
|
+
},
|
|
1050
|
+
expectedOutput: "Array of the 10 slowest span instances",
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
description: "Get spans filtered by OS with pagination",
|
|
1054
|
+
parameters: {
|
|
1055
|
+
spanGroupId: "[HttpClient]GET-api.example.com",
|
|
1056
|
+
sort: "timestamp",
|
|
1057
|
+
filters: {
|
|
1058
|
+
"os.name": [{ type: "eq", value: "iOS" }],
|
|
1059
|
+
},
|
|
1060
|
+
nextUrl: "/projects/123/spans?offset=30&per_page=30",
|
|
1061
|
+
},
|
|
1062
|
+
expectedOutput: "Array of spans from iOS devices with next page navigation",
|
|
1063
|
+
},
|
|
1064
|
+
],
|
|
1065
|
+
hints: [
|
|
1066
|
+
"Sort by duration descending to find the slowest instances",
|
|
1067
|
+
"Each span includes trace ID for further investigation",
|
|
1068
|
+
],
|
|
1069
|
+
}, async (args, _extra) => {
|
|
1070
|
+
const params = listSpansInputSchema.parse(args);
|
|
1071
|
+
const project = await this.getInputProject(params.projectId);
|
|
1072
|
+
const result = await this.projectApi.listSpansBySpanGroupId(project.id, params.spanGroupId, params.filters, params.sort, params.direction, params.perPage, params.nextUrl);
|
|
1073
|
+
return {
|
|
1074
|
+
content: [
|
|
1075
|
+
{
|
|
1076
|
+
type: "text",
|
|
1077
|
+
text: JSON.stringify({
|
|
1078
|
+
data: result.body,
|
|
1079
|
+
next_url: result.nextUrl,
|
|
1080
|
+
count: result.body?.length,
|
|
1081
|
+
}),
|
|
1082
|
+
},
|
|
1083
|
+
],
|
|
1084
|
+
};
|
|
1085
|
+
});
|
|
1086
|
+
const getTraceInputSchema = z.object({
|
|
1087
|
+
projectId: toolInputParameters.projectId,
|
|
1088
|
+
traceId: z.string().describe("Trace ID"),
|
|
1089
|
+
from: z.string().describe("Start time (ISO 8601 format)"),
|
|
1090
|
+
to: z.string().describe("End time (ISO 8601 format)"),
|
|
1091
|
+
targetSpanId: z
|
|
1092
|
+
.string()
|
|
1093
|
+
.optional()
|
|
1094
|
+
.describe("Optional target span ID to focus on"),
|
|
1095
|
+
perPage: toolInputParameters.perPage,
|
|
1096
|
+
nextUrl: toolInputParameters.nextUrl,
|
|
1097
|
+
});
|
|
1098
|
+
register({
|
|
1099
|
+
title: "Get Trace",
|
|
1100
|
+
summary: "Get all spans within a specific trace",
|
|
1101
|
+
purpose: "View the complete trace of operations for a request/transaction",
|
|
1102
|
+
useCases: [
|
|
1103
|
+
"Debug slow requests by viewing all operations in the trace",
|
|
1104
|
+
"Understand the flow of a request through the system",
|
|
1105
|
+
"Identify bottlenecks in distributed systems",
|
|
1106
|
+
],
|
|
1107
|
+
inputSchema: getTraceInputSchema,
|
|
1108
|
+
examples: [
|
|
1109
|
+
{
|
|
1110
|
+
description: "Get all spans for a trace",
|
|
1111
|
+
parameters: {
|
|
1112
|
+
traceId: "abc123",
|
|
1113
|
+
from: "2024-01-01T00:00:00Z",
|
|
1114
|
+
to: "2024-01-01T23:59:59Z",
|
|
1115
|
+
},
|
|
1116
|
+
expectedOutput: "Array of all spans in the trace with timing and hierarchy",
|
|
1117
|
+
},
|
|
1118
|
+
{
|
|
1119
|
+
description: "Get spans for a trace with pagination and target span",
|
|
1120
|
+
parameters: {
|
|
1121
|
+
traceId: "def456",
|
|
1122
|
+
from: "2024-01-01T00:00:00Z",
|
|
1123
|
+
to: "2024-01-01T23:59:59Z",
|
|
1124
|
+
targetSpanId: "span-789",
|
|
1125
|
+
perPage: 50,
|
|
1126
|
+
},
|
|
1127
|
+
expectedOutput: "Array of up to 50 spans focused around the target span",
|
|
1128
|
+
},
|
|
1129
|
+
],
|
|
1130
|
+
hints: [
|
|
1131
|
+
"Traces show the complete execution path of a request",
|
|
1132
|
+
"Use from/to parameters to narrow the time window",
|
|
1133
|
+
"targetSpanId can be used to focus on a specific span in the trace",
|
|
1134
|
+
],
|
|
1135
|
+
}, async (args, _extra) => {
|
|
1136
|
+
const params = getTraceInputSchema.parse(args);
|
|
1137
|
+
const project = await this.getInputProject(params.projectId);
|
|
1138
|
+
if (!params.traceId || !params.from || !params.to) {
|
|
1139
|
+
throw new ToolError("traceId, from, and to are required");
|
|
1140
|
+
}
|
|
1141
|
+
const result = await this.projectApi.listSpansByTraceId(project.id, params.traceId, params.from, params.to, params.targetSpanId, params.perPage, params.nextUrl);
|
|
1142
|
+
return {
|
|
1143
|
+
content: [
|
|
1144
|
+
{
|
|
1145
|
+
type: "text",
|
|
1146
|
+
text: JSON.stringify({
|
|
1147
|
+
data: result.body,
|
|
1148
|
+
next_url: result.nextUrl,
|
|
1149
|
+
count: result.body?.length,
|
|
1150
|
+
}),
|
|
1151
|
+
},
|
|
1152
|
+
],
|
|
1153
|
+
};
|
|
1154
|
+
});
|
|
1155
|
+
const listTraceFieldsInputSchema = z.object({
|
|
1156
|
+
projectId: toolInputParameters.projectId,
|
|
1157
|
+
});
|
|
1158
|
+
// Similar to event filters, consider caching
|
|
1159
|
+
register({
|
|
1160
|
+
title: "List Trace Fields",
|
|
1161
|
+
summary: "Get available trace fields/attributes for filtering",
|
|
1162
|
+
purpose: "Discover what custom attributes are available for filtering",
|
|
1163
|
+
useCases: [
|
|
1164
|
+
"Find available custom attributes for performance filtering",
|
|
1165
|
+
"Understand what metadata is attached to traces",
|
|
1166
|
+
"Build dynamic filters based on available fields",
|
|
1167
|
+
],
|
|
1168
|
+
inputSchema: listTraceFieldsInputSchema,
|
|
1169
|
+
examples: [
|
|
1170
|
+
{
|
|
1171
|
+
description: "Get all trace fields",
|
|
1172
|
+
parameters: {},
|
|
1173
|
+
expectedOutput: "Array of field names and types available for filtering",
|
|
1174
|
+
},
|
|
1175
|
+
],
|
|
1176
|
+
hints: [
|
|
1177
|
+
"Trace fields are custom attributes added to spans",
|
|
1178
|
+
"Use these fields for filtering other performance queries",
|
|
1179
|
+
],
|
|
1180
|
+
}, async (args, _extra) => {
|
|
1181
|
+
const params = listTraceFieldsInputSchema.parse(args);
|
|
1182
|
+
const project = await this.getInputProject(params.projectId);
|
|
1183
|
+
const traceFields = await this.getProjectTraceFields(project);
|
|
1184
|
+
return {
|
|
1185
|
+
content: [{ type: "text", text: JSON.stringify(traceFields) }],
|
|
1186
|
+
};
|
|
1187
|
+
});
|
|
1188
|
+
const getNetworkGroupingInputSchema = z.object({
|
|
1189
|
+
projectId: toolInputParameters.projectId,
|
|
1190
|
+
});
|
|
1191
|
+
register({
|
|
1192
|
+
title: "Get Network Endpoint Groupings",
|
|
1193
|
+
summary: "Get the network endpoint grouping rules for a project",
|
|
1194
|
+
purpose: "Retrieve the URL patterns used to group network spans for performance monitoring",
|
|
1195
|
+
useCases: [
|
|
1196
|
+
"View current network endpoint grouping configuration",
|
|
1197
|
+
"Understand how network requests are being grouped in performance monitoring",
|
|
1198
|
+
"Check grouping patterns before making updates",
|
|
1199
|
+
],
|
|
1200
|
+
inputSchema: getNetworkGroupingInputSchema,
|
|
1201
|
+
examples: [
|
|
1202
|
+
{
|
|
1203
|
+
description: "Get network grouping rules for a project",
|
|
1204
|
+
parameters: {},
|
|
1205
|
+
expectedOutput: "Array of endpoint URL patterns",
|
|
1206
|
+
},
|
|
1207
|
+
],
|
|
1208
|
+
hints: [
|
|
1209
|
+
"Network grouping patterns help consolidate similar requests into single span groups",
|
|
1210
|
+
"Patterns use OpenAPI path templating syntax with curly braces for path parameters (e.g., /users/{userId})",
|
|
1211
|
+
"Wildcards (*) can be used in domains to match multiple subdomains (e.g., https://*.example.com)",
|
|
1212
|
+
],
|
|
1213
|
+
readOnly: true,
|
|
1214
|
+
idempotent: true,
|
|
1215
|
+
}, async (args, _extra) => {
|
|
1216
|
+
const params = getNetworkGroupingInputSchema.parse(args);
|
|
1217
|
+
const project = await this.getInputProject(params.projectId);
|
|
1218
|
+
const result = await this.projectApi.getProjectNetworkGroupingRuleset(project.id);
|
|
1219
|
+
return {
|
|
1220
|
+
content: [
|
|
1221
|
+
{ type: "text", text: JSON.stringify(result.body.endpoints || []) },
|
|
1222
|
+
],
|
|
1223
|
+
};
|
|
1224
|
+
});
|
|
1225
|
+
const setNetworkGroupingInputSchema = z.object({
|
|
1226
|
+
projectId: toolInputParameters.projectId,
|
|
1227
|
+
endpoints: z
|
|
1228
|
+
.array(z.string())
|
|
1229
|
+
.describe("Array of URL patterns by which network spans are grouped. " +
|
|
1230
|
+
"Endpoints follow OpenAPI path templating syntax (https://swagger.io/specification/#path-templating) where path parameters use curly braces (e.g., /users/{id}). " +
|
|
1231
|
+
"If you encounter colon-prefixed parameters (e.g., :userId from Express/React Router), convert them to curly braces (e.g., {userId}). " +
|
|
1232
|
+
"Wildcards (*) can be used in domains (e.g., https://*.example.com) to match multiple subdomains."),
|
|
1233
|
+
});
|
|
1234
|
+
register({
|
|
1235
|
+
title: "Set Network Endpoint Groupings",
|
|
1236
|
+
summary: "Set the network endpoint grouping rules for a project",
|
|
1237
|
+
purpose: "Configure URL patterns to control how network spans are grouped in performance monitoring",
|
|
1238
|
+
useCases: [
|
|
1239
|
+
"Consolidate similar API endpoints into single span groups",
|
|
1240
|
+
"Group dynamic URLs using path parameters (e.g., /api/users/{userId} groups /api/users/123, /api/users/456)",
|
|
1241
|
+
"Match multiple subdomains using wildcards (e.g., https://*.example.com groups api.example.com, cdn.example.com)",
|
|
1242
|
+
"Simplify performance monitoring by reducing span group clutter",
|
|
1243
|
+
],
|
|
1244
|
+
inputSchema: setNetworkGroupingInputSchema,
|
|
1245
|
+
examples: [
|
|
1246
|
+
{
|
|
1247
|
+
description: "Group API endpoints with path parameters",
|
|
1248
|
+
parameters: {
|
|
1249
|
+
endpoints: [
|
|
1250
|
+
"/api/users/{userId}",
|
|
1251
|
+
"/api/products/{productId}",
|
|
1252
|
+
"/api/orders/{orderId}/items/{itemId}",
|
|
1253
|
+
],
|
|
1254
|
+
},
|
|
1255
|
+
expectedOutput: "Success response confirming the update",
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
description: "Group endpoints with domain wildcards and path parameters",
|
|
1259
|
+
parameters: {
|
|
1260
|
+
endpoints: [
|
|
1261
|
+
"https://*.example.com/api/v1/{resourceId}",
|
|
1262
|
+
"https://api.example.com/v2/users/{userId}",
|
|
1263
|
+
"/graphql",
|
|
1264
|
+
],
|
|
1265
|
+
},
|
|
1266
|
+
expectedOutput: "Success response confirming the update",
|
|
1267
|
+
},
|
|
1268
|
+
{
|
|
1269
|
+
description: "Convert colon-prefixed parameters to curly braces (e.g., from Express/React Router)",
|
|
1270
|
+
parameters: {
|
|
1271
|
+
endpoints: [
|
|
1272
|
+
"/{organizationSlug}/{projectSlug}/performance/view-load",
|
|
1273
|
+
"/api/{version}/items/{itemId}",
|
|
1274
|
+
],
|
|
1275
|
+
},
|
|
1276
|
+
expectedOutput: "Success response confirming the update",
|
|
1277
|
+
},
|
|
1278
|
+
],
|
|
1279
|
+
hints: [
|
|
1280
|
+
"Use Get Network Grouping first to see current patterns",
|
|
1281
|
+
"Use OpenAPI path templating with curly braces for path parameters: /users/{userId}, /orders/{orderId}/items/{itemId}",
|
|
1282
|
+
"Convert colon-prefixed parameters to curly braces: :organizationSlug becomes {organizationSlug}, :projectSlug becomes {projectSlug}",
|
|
1283
|
+
"Wildcards (*) can be used in domains to match subdomains: https://*.example.com/api",
|
|
1284
|
+
"This replaces all existing patterns - include all patterns you want to keep",
|
|
1285
|
+
"Well-designed patterns reduce noise in performance monitoring",
|
|
1286
|
+
],
|
|
1287
|
+
readOnly: false,
|
|
1288
|
+
idempotent: true,
|
|
1289
|
+
}, async (args, _extra) => {
|
|
1290
|
+
const params = setNetworkGroupingInputSchema.parse(args);
|
|
1291
|
+
const project = await this.getInputProject(params.projectId);
|
|
1292
|
+
const result = await this.projectApi.updateProjectNetworkGroupingRuleset(project.id, params.endpoints);
|
|
1293
|
+
return {
|
|
1294
|
+
content: [
|
|
1295
|
+
{
|
|
1296
|
+
type: "text",
|
|
1297
|
+
text: JSON.stringify({
|
|
1298
|
+
success: result.status === 200 || result.status === 204,
|
|
1299
|
+
projectId: project.id,
|
|
1300
|
+
endpoints: params.endpoints,
|
|
1301
|
+
}),
|
|
1302
|
+
},
|
|
1303
|
+
],
|
|
1304
|
+
};
|
|
1305
|
+
});
|
|
803
1306
|
}
|
|
804
1307
|
registerResources(register) {
|
|
805
1308
|
register("event", "{id}", async (uri, variables, _extra) => {
|
|
@@ -3,25 +3,32 @@ const filterValueSchema = z.object({
|
|
|
3
3
|
type: z.enum(["eq", "ne", "empty"]),
|
|
4
4
|
value: z.union([z.string(), z.boolean(), z.number()]),
|
|
5
5
|
});
|
|
6
|
+
const filtersSchema = z.record(z.array(filterValueSchema));
|
|
6
7
|
/**
|
|
7
8
|
* A collection of input parameter schemas for reuse between tools.
|
|
8
9
|
* Add new entries when common parameters are identified.
|
|
9
10
|
*/
|
|
10
11
|
export const toolInputParameters = {
|
|
11
12
|
empty: z.object({}).describe("No parameters are required for this tool"),
|
|
13
|
+
buildId: z.string().describe("Unique identifier of the app build"),
|
|
14
|
+
errorId: z.string().describe("Unique identifier of the error"),
|
|
15
|
+
eventId: z.string().describe("Unique identifier of the event"),
|
|
12
16
|
projectId: z
|
|
13
17
|
.string()
|
|
14
18
|
.optional()
|
|
15
|
-
.describe("
|
|
16
|
-
|
|
17
|
-
releaseId: z.string().describe("ID of the release"),
|
|
18
|
-
buildId: z.string().describe("ID of the build"),
|
|
19
|
+
.describe("Unique identifier of the project. This is optional if a current project is set and is used to set the current project for BugSnag tools."),
|
|
20
|
+
releaseId: z.string().describe("Unique identifier of the app release"),
|
|
19
21
|
direction: z
|
|
20
22
|
.enum(["asc", "desc"])
|
|
21
23
|
.describe("Sort direction for ordering results")
|
|
22
24
|
.default("desc"),
|
|
23
|
-
|
|
24
|
-
.
|
|
25
|
+
performanceFilters: filtersSchema
|
|
26
|
+
.describe("Apply filters to narrow down the span group list. Use the List Trace Fields tool to discover available filter fields. " +
|
|
27
|
+
"Time filters support extended ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h).")
|
|
28
|
+
.default({
|
|
29
|
+
"span.since": [{ type: "eq", value: "7d" }],
|
|
30
|
+
}),
|
|
31
|
+
filters: filtersSchema
|
|
25
32
|
.describe("Apply filters to narrow down the error list. Use the List Project Event Filters tool to discover available filter fields. " +
|
|
26
33
|
"Time filters support extended ISO 8601 format (e.g. 2018-05-20T00:00:00Z) or relative format (e.g. 7d, 24h).")
|
|
27
34
|
.default({
|
|
@@ -48,4 +55,5 @@ export const toolInputParameters = {
|
|
|
48
55
|
.enum(["first_seen", "last_seen", "events", "users", "unsorted"])
|
|
49
56
|
.describe("Field to sort the errors by")
|
|
50
57
|
.default("last_seen"),
|
|
58
|
+
spanGroupId: z.string().describe("ID of the span group"),
|
|
51
59
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
2
|
import { clientRegistry } from "./client-registry.js";
|
|
3
|
+
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "./info.js";
|
|
3
4
|
import { SmartBearMcpServer } from "./server.js";
|
|
4
5
|
/**
|
|
5
6
|
* Generate a dynamic error message listing all available clients and their required env vars
|
|
@@ -22,6 +23,10 @@ function getNoConfigErrorMessage() {
|
|
|
22
23
|
* Run server in STDIO mode (default)
|
|
23
24
|
*/
|
|
24
25
|
export async function runStdioMode() {
|
|
26
|
+
if (process.argv.includes("--version")) {
|
|
27
|
+
console.log(`${MCP_SERVER_NAME}: v${MCP_SERVER_VERSION}`);
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
25
30
|
const server = new SmartBearMcpServer();
|
|
26
31
|
// Setup clients from environment variables
|
|
27
32
|
const configuredCount = await clientRegistry.configure(server, (client, key) => {
|