Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 44 additions & 47 deletions src/bugsnag/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,25 @@ import type {
} from "./client/api/index";
import { ProjectAPI } from "./client/api/Project";
import type { FilterObject } from "./client/filters";
import { GetError } from "./tool/error/get-error";
import { ListProjectErrors } from "./tool/error/list-project-errors";
import { UpdateError } from "./tool/error/update-error";
import { GetEvent } from "./tool/event/get-event";
import { GetEventDetailsFromDashboardUrl } from "./tool/event/get-event-details-from-dashboard-url";
import { GetNetworkEndpointGroupings } from "./tool/performance/get-network-endpoint-groupings";
import { GetSpanGroup } from "./tool/performance/get-span-group";
import { GetTrace } from "./tool/performance/get-trace";
import { ListSpanGroups } from "./tool/performance/list-span-groups";
import { ListSpans } from "./tool/performance/list-spans";
import { ListTraceFields } from "./tool/performance/list-trace-fields";
import { SetNetworkEndpointGroupings } from "./tool/performance/set-network-endpoint-groupings";
import { GetCurrentProject } from "./tool/project/get-current-project";
import { ListProjectEventFilters } from "./tool/project/list-project-event-filters";
import { ListProjects } from "./tool/project/list-projects";
import { GetBuild } from "./tool/release/get-build";
import { GetRelease } from "./tool/release/get-release";
import { ListReleases } from "./tool/release/list-releases";
import { eventResource } from "./resource/event-resource";
import { getError } from "./tool/error/get-error";
import { listProjectErrors } from "./tool/error/list-project-errors";
import { updateError } from "./tool/error/update-error";
import { getEvent } from "./tool/event/get-event";
import { getEventDetailsFromDashboardUrl } from "./tool/event/get-event-details-from-dashboard-url";
import { getNetworkEndpointGroupings } from "./tool/performance/get-network-endpoint-groupings";
import { getSpanGroup } from "./tool/performance/get-span-group";
import { getTrace } from "./tool/performance/get-trace";
import { listSpanGroups } from "./tool/performance/list-span-groups";
import { listSpans } from "./tool/performance/list-spans";
import { listTraceFields } from "./tool/performance/list-trace-fields";
import { setNetworkEndpointGroupings } from "./tool/performance/set-network-endpoint-groupings";
import { getCurrentProject } from "./tool/project/get-current-project";
import { listProjectEventFilters } from "./tool/project/list-project-event-filters";
import { listProjects } from "./tool/project/list-projects";
import { getBuild } from "./tool/release/get-build";
import { getRelease } from "./tool/release/get-release";
import { listReleases } from "./tool/release/list-releases";

const HUB_PREFIX = "00000";
const DEFAULT_DOMAIN = "bugsnag.com";
Expand Down Expand Up @@ -368,41 +369,37 @@ export class BugsnagClient implements Client {
getInput: GetInputFunction,
): Promise<void> {
const tools = [
new GetCurrentProject(this),
new ListProjects(this),
new ListProjectEventFilters(this),
new GetError(this),
new ListProjectErrors(this),
new UpdateError(this, getInput),
new GetEvent(this),
new GetEventDetailsFromDashboardUrl(this),
new ListReleases(this),
new GetRelease(this),
new GetBuild(this),
new ListSpanGroups(this),
new GetSpanGroup(this),
new ListSpans(this),
new GetTrace(this),
new ListTraceFields(this),
new GetNetworkEndpointGroupings(this),
new SetNetworkEndpointGroupings(this),
getCurrentProject,
listProjects,
listProjectEventFilters,
getError,
listProjectErrors,
getEvent,
getEventDetailsFromDashboardUrl,
listReleases,
getRelease,
getBuild,
listSpanGroups,
getSpanGroup,
listSpans,
getTrace,
listTraceFields,
getNetworkEndpointGroupings,
setNetworkEndpointGroupings,
];

for (const tool of tools) {
register(tool.specification, tool.handle);
tool.register(this, register);
}

const interactiveTools = [updateError];

for (const tool of interactiveTools) {
tool.register(this, register, getInput);
}
}

registerResources(register: RegisterResourceFunction): void {
register("event", "{id}", async (uri, variables, _extra) => {
return {
contents: [
{
uri: uri.href,
text: JSON.stringify(await this.getEvent(variables.id as string)),
},
],
};
});
eventResource.register(this, register);
}
}
19 changes: 19 additions & 0 deletions src/bugsnag/resource/event-resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Resource } from "../../common/resources";
import type { BugsnagClient } from "../client";

export const eventResource = new Resource(
{
name: "event",
path: "{id}",
},
(client: BugsnagClient) => async (uri, variables, _extra) => {
return {
contents: [
{
uri: uri.href,
text: JSON.stringify(await client.getEvent(variables.id)),
},
],
};
},
);
32 changes: 14 additions & 18 deletions src/bugsnag/tool/error/get-error.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { ZodRawShape } from "zod";
import { z } from "zod";
import { Tool, ToolError } from "../../../common/tools";
import type { ToolParams } from "../../../common/types";
import { ToolError, TypesafeTool } from "../../../common/tools";
import type { BugsnagClient } from "../../client";
import { type FilterObject, toUrlSearchParams } from "../../client/filters";
import { toolInputParameters } from "../../input-schemas";
Expand All @@ -19,8 +16,8 @@ const inputSchema = z.object({
});

// Fetches full details for a single error including aggregated stats, the latest event, pivots, and a dashboard URL.
export class GetError extends Tool<BugsnagClient> {
specification: ToolParams = {
export const getError = new TypesafeTool(
{
title: "Get Error",
summary:
"Get full details on an error, including aggregated and summarized data across all events (occurrences) and details of the latest event (occurrence), such as breadcrumbs, metadata and the stacktrace. Use the filters parameter to narrow down the summaries further.",
Expand Down Expand Up @@ -56,13 +53,12 @@ export class GetError extends Tool<BugsnagClient> {
"If you used a filter to get this error, you can pass the same filters here to restrict the results or apply further filters",
"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",
],
};

handle: ToolCallback<ZodRawShape> = async (args, _extra) => {
const params = inputSchema.parse(args);
const project = await this.client.getInputProject(params.projectId);
},
(client: BugsnagClient) => async (args, _extra) => {
const params = args;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either rename the usages to args or rename the arg to params. Same for all the tools

Suggested change
(client: BugsnagClient) => async (args, _extra) => {
const params = args;
(client: BugsnagClient) => async (params, _extra) => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated across the tools

const project = await client.getInputProject(params.projectId);
const errorDetails = (
await this.client.errorsApi.viewErrorOnProject(project.id, params.errorId)
await client.errorsApi.viewErrorOnProject(project.id, params.errorId)
).body;
if (!errorDetails) {
throw new ToolError(
Expand All @@ -75,13 +71,13 @@ export class GetError extends Tool<BugsnagClient> {
...params.filters,
};

await this.client.validateEventFields(project, filters);
await client.validateEventFields(project, filters);

// Get the latest event for this error using the events endpoint with filters
let latestEvent = null;
try {
const latestEvents = (
await this.client.errorsApi.listEventsOnProject(
await client.errorsApi.listEventsOnProject(
project.id,
null,
"timestamp",
Expand All @@ -105,14 +101,14 @@ export class GetError extends Tool<BugsnagClient> {
latest_event: latestEvent,
pivots:
(
await this.client.errorsApi.getPivotValuesOnAnError(
await client.errorsApi.getPivotValuesOnAnError(
project.id,
params.errorId,
filters,
5,
)
).body || [],
url: await this.client.getErrorUrl(
url: await client.getErrorUrl(
project,
params.errorId,
toUrlSearchParams(filters).toString(),
Expand All @@ -121,5 +117,5 @@ export class GetError extends Tool<BugsnagClient> {
return {
content: [{ type: "text", text: JSON.stringify(content) }],
};
};
}
},
);
26 changes: 11 additions & 15 deletions src/bugsnag/tool/error/list-project-errors.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { ZodRawShape } from "zod";
import { z } from "zod";
import { Tool } from "../../../common/tools";
import type { ToolParams } from "../../../common/types";
import { TypesafeTool } from "../../../common/tools";
import type { BugsnagClient } from "../../client";
import type { FilterObject } from "../../client/filters";
import { toolInputParameters } from "../../input-schemas";
Expand All @@ -20,8 +17,8 @@ const inputSchema = z.object({
});

// Lists errors in a project with optional filters, sorting, and pagination.
export class ListProjectErrors extends Tool<BugsnagClient> {
specification: ToolParams = {
export const listProjectErrors = new TypesafeTool(
{
title: "List Project Errors",
summary:
"List and search errors in a project using customizable filters and pagination",
Expand Down Expand Up @@ -84,11 +81,10 @@ export class ListProjectErrors extends Tool<BugsnagClient> {
"If the output contains a 'next_url' value, there are more results available - call this tool again supplying the next URL as a parameter to retrieve the next page.",
"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.",
],
};

handle: ToolCallback<ZodRawShape> = async (args, _extra) => {
const params = inputSchema.parse(args);
const project = await this.client.getInputProject(params.projectId);
},
(client: BugsnagClient) => async (args, _extra) => {
const params = args;
const project = await client.getInputProject(params.projectId);

const filters: FilterObject = {
"event.since": [{ type: "eq", value: "30d" }],
Expand All @@ -97,9 +93,9 @@ export class ListProjectErrors extends Tool<BugsnagClient> {
};

// Validate filter keys against cached event fields
await this.client.validateEventFields(project, filters);
await client.validateEventFields(project, filters);

const response = await this.client.errorsApi.listProjectErrors(
const response = await client.errorsApi.listProjectErrors(
project.id,
null,
params.sort,
Expand All @@ -118,5 +114,5 @@ export class ListProjectErrors extends Tool<BugsnagClient> {
return {
content: [{ type: "text", text: JSON.stringify(result) }],
};
};
}
},
);
Loading
Loading