Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
32 changes: 32 additions & 0 deletions packages/mcp-core/src/api-client/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,38 @@ import { http, HttpResponse } from "msw";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { ConfigurationError } from "../errors";
import { SentryApiService } from "./client";
import { ApiValidationError } from "./errors";
import { z } from "zod";

describe("validateEvents error bodies", () => {
afterEach(() => mswServer.resetHandlers());

it.each([400, 200])(
"preserves error classification for HTTP %i",
async (status) => {
mswServer.use(
http.get(
"https://sentry.io/api/0/organizations/test-org/events/validate/",
() => HttpResponse.json({ detail: "Invalid query" }, { status }),
),
);
const api = new SentryApiService({
host: "sentry.io",
accessToken: "test-token",
});
const result = api.validateEvents({ organizationSlug: "test-org" });
if (status === 400) {
await expect(result).rejects.toBeInstanceOf(ApiValidationError);
await expect(result).rejects.toMatchObject({
status: 400,
detail: "Invalid query",
});
} else {
await expect(result).rejects.toBeInstanceOf(z.ZodError);
}
},
);
});

describe("getIssueUrl", () => {
it("should work with sentry.io", () => {
Expand Down
15 changes: 14 additions & 1 deletion packages/mcp-core/src/api-client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3390,7 +3390,20 @@ export class SentryApiService {
{ ...opts, allowStatuses: [400] },
);
const body = await this.parseJsonResponse(response);
return EventsValidationResponseSchema.parse(body);
try {
return EventsValidationResponseSchema.parse(body);
} catch (err) {
if (response.status === 400 && err instanceof z.ZodError) {
// The API returned a 400 with a generic error body (e.g. {detail: "..."})
// rather than a structured validation result. Treat it as an API client error.
const detail =
body && typeof body === "object" && "detail" in body
? String((body as { detail: unknown }).detail)
: "Invalid request";
throw createApiError(detail, 400, detail, body);
}
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
throw err;
}
}

private async fetchTraceItemAttributes(
Expand Down
Loading