Skip to content

Commit d366df4

Browse files
fix(catalog): Prevent ApiClientError from being logged as server bug (#1155)
The `update_project` tool in `packages/mcp-core/src/tools/catalog/update-project.ts` was unconditionally calling `logIssue(err)` in its catch block. This led to `ApiClientError` instances (which represent 4xx HTTP responses due to invalid user input, e.g., bad slugs) being reported to Sentry as high-priority server bugs. This change modifies the catch block to check if the error is an `ApiClientError`. If it is, the error is re-thrown directly, allowing the MCP framework's existing error handling (which correctly formats `ApiClientError` as an "Input Error" for the user and does not log it to Sentry) to take over. This prevents expected user input validation errors from cluttering Sentry with false positives. Fixes [MCP-SERVER-G3D](https://sentry.sentry.io/issues/7608940636/?seerDrawer=true) <sub>Comment `@sentry <feedback>` on this PR to have Autofix iterate on the changes.</sub> --------- Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com> Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
1 parent b0863cd commit d366df4

2 files changed

Lines changed: 37 additions & 0 deletions

File tree

‎packages/mcp-core/src/tools/catalog/update-project.test.ts‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { mswServer } from "@sentry/mcp-server-mocks";
22
import { http, HttpResponse } from "msw";
33
import { afterEach, describe, it, expect } from "vitest";
44
import { UserInputError } from "../../errors.js";
5+
import { ApiClientError } from "../../api-client";
56
import updateProject from "./update-project.js";
67
import { prepareToolParams } from "../catalog-runtime/availability";
78
import {
@@ -23,6 +24,38 @@ describe("update_project", () => {
2324
mswServer.resetHandlers();
2425
});
2526

27+
it.each([400, 403, 404])(
28+
"preserves API client errors for HTTP %i",
29+
async (status) => {
30+
mswServer.use(
31+
http.put(
32+
"https://sentry.io/api/0/projects/sentry-mcp-evals/cloudflare-mcp/",
33+
() =>
34+
HttpResponse.json(
35+
{ detail: "Project update rejected" },
36+
{ status },
37+
),
38+
),
39+
);
40+
const result = updateProject.handler(
41+
{
42+
organizationSlug: "sentry-mcp-evals",
43+
projectSlug: "cloudflare-mcp",
44+
name: "New name",
45+
slug: null,
46+
platform: null,
47+
regionUrl: null,
48+
},
49+
context,
50+
);
51+
await expect(result).rejects.toBeInstanceOf(ApiClientError);
52+
await expect(result).rejects.toMatchObject({
53+
status,
54+
message: "Project update rejected",
55+
});
56+
},
57+
);
58+
2659
it("updates name and platform", async () => {
2760
mswServer.use(
2861
http.put(

‎packages/mcp-core/src/tools/catalog/update-project.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { apiServiceFromContext } from "../../internal/tool-helpers/api";
66
import { structuredResult } from "../../internal/tool-helpers/results";
77
import { logIssue } from "../../telem/logging";
88
import { UserInputError } from "../../errors";
9+
import { ApiClientError } from "../../api-client";
910
import type { ServerContext } from "../../types";
1011
import type { Project } from "../../api-client/index";
1112
import {
@@ -116,6 +117,9 @@ export default defineTool({
116117
platform: params.platform,
117118
});
118119
} catch (err) {
120+
if (err instanceof ApiClientError) {
121+
throw err;
122+
}
119123
logIssue(err);
120124
throw new Error(
121125
`Failed to update project ${params.projectSlug}: ${err instanceof Error ? err.message : "Unknown error"}`,

0 commit comments

Comments
 (0)