From 59642153f37f8d91d9d17e7d285e14dacd4b5b10 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 17:39:20 +0000 Subject: [PATCH 1/2] fix(tools): use resolved numeric IDs for issue detail requests Keep event, external-link, and Seer lookups on the resolved numeric issue ID so legacy short-ID resolution cannot break a successful lookup. Co-Authored-By: David Cramer --- docs/contributing/api-patterns.md | 7 ++ .../get-issue-details.numeric-id.test.ts | 112 ++++++++++++++++++ .../tools/catalog/get-issue-details.test.ts | 70 +++++------ .../src/tools/catalog/get-issue-details.ts | 10 +- packages/mcp-server-mocks/src/index.ts | 14 +++ 5 files changed, 173 insertions(+), 40 deletions(-) create mode 100644 packages/mcp-core/src/tools/catalog/get-issue-details.numeric-id.test.ts diff --git a/docs/contributing/api-patterns.md b/docs/contributing/api-patterns.md index ecbb46607..b52091fa9 100644 --- a/docs/contributing/api-patterns.md +++ b/docs/contributing/api-patterns.md @@ -63,6 +63,13 @@ await api.issues.update({ }); ``` +### Resolved Issue IDs + +After resolving an issue, use `String(issue.id)` for follow-up API requests, +including events, external links, and Seer analysis. Do not switch back to +`issue.shortId`: short-ID resolution can fail for legacy mixed-case project +slugs even when numeric-ID lookup succeeds. Keep short IDs for display. + ### Multi-Region Support Sentry uses region-specific URLs: diff --git a/packages/mcp-core/src/tools/catalog/get-issue-details.numeric-id.test.ts b/packages/mcp-core/src/tools/catalog/get-issue-details.numeric-id.test.ts new file mode 100644 index 000000000..b76b7bff3 --- /dev/null +++ b/packages/mcp-core/src/tools/catalog/get-issue-details.numeric-id.test.ts @@ -0,0 +1,112 @@ +import { + autofixStateFixture, + eventsFixture, + issueFixture, + mswServer, +} from "@sentry/mcp-server-mocks"; +import { http, HttpResponse } from "msw"; +import { describe, expect, it } from "vitest"; +import getIssueDetails from "./get-issue-details"; +import getSentryResource from "./get-sentry-resource"; + +const organizationSlug = "sentry-mcp-evals"; +const issueId = String(issueFixture.id); +const eventId = eventsFixture.id; +const issueUrl = `https://${organizationSlug}.sentry.io/issues/${issueId}/`; +const context = { + constraints: { organizationSlug: undefined }, + accessToken: "access-token", + userId: "1", +}; + +// Short IDs for legacy mixed-case project slugs can fail to resolve even though +// the numeric ID works. Reject every short-ID follow-up, including optional data. +describe("issue details with an unresolvable short ID", () => { + it.each([ + { + name: "numeric issue ID", + call: () => + getIssueDetails.handler( + { organizationSlug, issueId, regionUrl: null }, + context, + ), + }, + { + name: "issue URL", + call: () => + getIssueDetails.handler({ issueUrl, regionUrl: null }, context), + }, + { + name: "numeric issue ID and explicit event ID", + call: () => + getIssueDetails.handler( + { organizationSlug, issueId, eventId, regionUrl: null }, + context, + ), + }, + { + name: "event ID resolved through issue search", + call: () => + getIssueDetails.handler( + { organizationSlug, eventId, regionUrl: null }, + context, + ), + }, + { + name: "resource issue URL", + call: () => getSentryResource.handler({ url: issueUrl }, context), + }, + { + name: "resource event URL", + call: () => + getSentryResource.handler( + { url: `${issueUrl}events/${eventId}/` }, + context, + ), + }, + ])("loads $name and enrichment by numeric ID", async ({ call }) => { + const requestedIssueIds: string[] = []; + const base = `https://sentry.io/api/0/organizations/${organizationSlug}`; + mswServer.use( + http.get(`${base}/issues/`, () => HttpResponse.json([issueFixture])), + http.get(`${base}/issues/:issueId/*`, ({ params, request }) => { + const requestedId = String(params.issueId); + requestedIssueIds.push(requestedId); + if (requestedId !== issueId) { + return HttpResponse.json( + { detail: "The requested resource does not exist" }, + { status: 404 }, + ); + } + const path = new URL(request.url).pathname; + if (path.includes("/events/")) { + return HttpResponse.json(eventsFixture); + } + if (path.endsWith("/external-issues/")) { + return HttpResponse.json([ + { + id: "123", + issueId, + serviceType: "github", + displayName: "example/app#123", + webUrl: "https://github.com/example/app/issues/123", + }, + ]); + } + if (path.endsWith("/autofix/")) { + return HttpResponse.json(autofixStateFixture); + } + return HttpResponse.json(issueFixture); + }), + ); + + const result = await call(); + + expect(result).toContain(`Issue ${issueFixture.shortId}`); + expect(result).toContain(eventsFixture.id); + expect(result).toContain("## Seer Analysis"); + expect(result).toContain("**example/app#123** (github)"); + expect(requestedIssueIds.length).toBeGreaterThanOrEqual(3); + expect(new Set(requestedIssueIds)).toEqual(new Set([issueId])); + }); +}); diff --git a/packages/mcp-core/src/tools/catalog/get-issue-details.test.ts b/packages/mcp-core/src/tools/catalog/get-issue-details.test.ts index 8715c80ed..abbb465dd 100644 --- a/packages/mcp-core/src/tools/catalog/get-issue-details.test.ts +++ b/packages/mcp-core/src/tools/catalog/get-issue-details.test.ts @@ -370,7 +370,7 @@ describe("get_issue_details", () => { } mswServer.use( http.get( - `${base}/events/latest/`, + `https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/${issue ? issue().id : "6507376925"}/events/latest/`, () => HttpResponse.json({ ...event(), @@ -411,7 +411,7 @@ describe("get_issue_details", () => { { once: true }, ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/7890123456/events/latest/", () => HttpResponse.json({ ...createPerformanceEvent(), @@ -448,7 +448,7 @@ describe("get_issue_details", () => { it("keeps the replay note when error events use formatted.content", async () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json({ ...createDefaultEvent(), @@ -485,7 +485,7 @@ describe("get_issue_details", () => { it("embeds the shared formatter's analysis in the Seer section when present", async () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/autofix/", () => HttpResponse.json({ autofix: { run_id: 7, status: "completed", blocks: [] }, @@ -536,7 +536,7 @@ describe("get_issue_details", () => { async ({ status, expected }) => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/autofix/", () => HttpResponse.json({ autofix: { run_id: 7, status, blocks: [] }, @@ -580,7 +580,7 @@ describe("get_issue_details", () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json(event), { once: true }, ), @@ -712,7 +712,7 @@ describe("get_issue_details", () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json(event), { once: true }, ), @@ -752,7 +752,7 @@ describe("get_issue_details", () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json(event), { once: true }, ), @@ -816,7 +816,7 @@ describe("get_issue_details", () => { { once: true }, ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/TEAM-ISSUE-001/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/123456789/events/latest/", () => HttpResponse.json(createDefaultEvent()), { once: true }, ), @@ -840,7 +840,7 @@ describe("get_issue_details", () => { it("lists threads and stacktrace lookup guidance only for multi-thread events", async () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json( createDefaultEvent({ @@ -936,7 +936,7 @@ describe("get_issue_details", () => { it("omits thread list and stacktrace lookup guidance for single-thread events", async () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json( createDefaultEvent({ @@ -1005,7 +1005,7 @@ describe("get_issue_details", () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json(event), { once: true }, ), @@ -1175,7 +1175,7 @@ describe("get_issue_details", () => { { once: true }, ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/7890123456/events/latest/", () => { // Create event with specific evidence data for this test const event = createPerformanceEvent(); @@ -1265,7 +1265,7 @@ describe("get_issue_details", () => { { once: true }, ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/7890123456/events/latest/", () => { // Create event with specific evidence data for this test const event = createPerformanceEvent(); @@ -1556,7 +1556,7 @@ describe("get_issue_details", () => { let autofixRequested = false; mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/autofix/", () => { autofixRequested = true; return HttpResponse.json({ autofix: null }); @@ -1586,7 +1586,7 @@ describe("get_issue_details", () => { let autofixRequested = false; mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/autofix/", () => { autofixRequested = true; return HttpResponse.json({ autofix: null }); @@ -1641,7 +1641,7 @@ describe("get_issue_details", () => { // Use mswServer.use to prepend a handler - MSW uses LIFO order mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/autofix/", () => HttpResponse.json(inProgressFixture), { once: true }, // Ensure this handler is only used once for this test ), @@ -1684,7 +1684,7 @@ describe("get_issue_details", () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/autofix/", () => HttpResponse.json(failedFixture), { once: true }, ), @@ -1739,7 +1739,7 @@ describe("get_issue_details", () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/autofix/", () => HttpResponse.json(needsInfoFixture), { once: true }, ), @@ -1778,7 +1778,7 @@ describe("get_issue_details", () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/DEFAULT-001/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/123456/events/latest/", () => HttpResponse.json(defaultEvent), ), http.get( @@ -1842,7 +1842,7 @@ describe("get_issue_details", () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/BLOG-CSP-4XC/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/4256774711/events/latest/", () => HttpResponse.json(cspEvent), ), http.get( @@ -1924,7 +1924,7 @@ describe("get_issue_details", () => { }), ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/MALFORMED-TAGS-001/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/123456/events/latest/", () => HttpResponse.json(eventWithMalformedTags), ), ); @@ -2014,7 +2014,7 @@ describe("get_issue_details", () => { }, ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CONTEXT-001/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/123456/events/latest/", () => { return HttpResponse.json(eventWithContext); }, @@ -2070,7 +2070,7 @@ describe("get_issue_details", () => { { once: true }, ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/MCP-SERVER-EQE/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6898891101/events/latest/", () => HttpResponse.json(regressedEventFixture), { once: true }, ), @@ -2152,7 +2152,7 @@ describe("get_issue_details", () => { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/external-issues/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/external-issues/", () => HttpResponse.json(mockExternalIssues), { once: true }, ), @@ -2216,7 +2216,7 @@ describe("get_issue_details", () => { mswServer.use( // More specific pattern for events (must come first to match before the issue pattern) http.get( - "https://sentry.io/api/0/organizations/*/issues/FUTURE-TYPE-001/events/latest/", + "https://sentry.io/api/0/organizations/*/issues/7777777777/events/latest/", () => { return HttpResponse.json(unsupportedEventFixture); }, @@ -2342,7 +2342,7 @@ describe("structuredContent", () => { function mockLatestEventWithFormatted(formatted: unknown) { mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json({ ...createDefaultEvent(), formatted }), ), ); @@ -2411,7 +2411,7 @@ describe("structuredContent", () => { // rendered for transactions, so a transaction must not take the structured path mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json({ ...createDefaultEvent(), @@ -2431,7 +2431,7 @@ describe("structuredContent", () => { // an issue whose only replay is attached would otherwise report no replays at all mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json({ ...createDefaultEvent(), @@ -2472,7 +2472,7 @@ describe("structuredContent", () => { // upstream schemas are passthrough, so anything not mapped must not appear mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json({ ...createDefaultEvent(), @@ -2480,7 +2480,7 @@ describe("structuredContent", () => { }), ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/external-issues/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/external-issues/", () => HttpResponse.json([ { @@ -2508,7 +2508,7 @@ describe("structuredContent", () => { // renders and the payload drops is a regression for every MCP user mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json({ ...createDefaultEvent(), @@ -2566,7 +2566,7 @@ describe("structuredContent", () => { }), ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/7890123456/events/latest/", () => HttpResponse.json({ ...createDefaultEvent(), @@ -2604,7 +2604,7 @@ describe("structuredContent", () => { }), ), http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/7890123456/events/latest/", () => HttpResponse.json({ ...createDefaultEvent(), @@ -2629,7 +2629,7 @@ describe("structuredContent", () => { ); mswServer.use( http.get( - "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/events/latest/", + "https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/6507376925/events/latest/", () => HttpResponse.json({ ...createDefaultEvent(), diff --git a/packages/mcp-core/src/tools/catalog/get-issue-details.ts b/packages/mcp-core/src/tools/catalog/get-issue-details.ts index 8eb9178b7..6d2f910c1 100644 --- a/packages/mcp-core/src/tools/catalog/get-issue-details.ts +++ b/packages/mcp-core/src/tools/catalog/get-issue-details.ts @@ -406,7 +406,7 @@ export default defineTool({ apiService .getEventForIssue({ organizationSlug: orgSlug, - issueId: issue.shortId, + issueId: String(issue.id), eventId, }) // Optionally enhance 404 errors with parameter context @@ -414,7 +414,7 @@ export default defineTool({ if (error instanceof ApiNotFoundError) { throw enhanceNotFoundError(error, { organizationSlug: orgSlug, - issueId: issue.shortId, + issueId: String(issue.id), eventId, }); } @@ -524,7 +524,7 @@ export default defineTool({ apiService .getLatestEventForIssue({ organizationSlug: orgSlug, - issueId: issue.shortId, + issueId: String(issue.id), }) .then(async (event) => ({ event, @@ -648,7 +648,7 @@ async function fetchIssueEnrichmentData({ seerEnabled, }), apiService - .getIssueExternalLinks({ organizationSlug, issueId: issue.shortId }) + .getIssueExternalLinks({ organizationSlug, issueId }) .catch(() => undefined), apiService .listReplayIdsForIssue({ @@ -691,7 +691,7 @@ async function maybeFetchAutofixState({ } return apiService - .getAutofixState({ organizationSlug, issueId: issue.shortId }) + .getAutofixState({ organizationSlug, issueId: String(issue.id) }) .catch(() => undefined); } diff --git a/packages/mcp-server-mocks/src/index.ts b/packages/mcp-server-mocks/src/index.ts index b0629e08a..a3fc063ec 100644 --- a/packages/mcp-server-mocks/src/index.ts +++ b/packages/mcp-server-mocks/src/index.ts @@ -1334,6 +1334,20 @@ export const restHandlers = buildHandlers([ path: "/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", fetch: () => HttpResponse.json({ autofix: null }), }, + // Issue details use the resolved numeric ID for enrichment requests. + ...[ + issueFixture.id, + "7890123456", // PERF-N1-001 + "123456", // DEFAULT-001 / CONTEXT-001 + "123456789", // TEAM-ISSUE-001 + "6898891101", // MCP-SERVER-EQE + "7777777777", // FUTURE-TYPE-001 + "4256774711", // BLOG-CSP-4XC + ].map((issueId) => ({ + method: "get" as const, + path: `/api/0/organizations/sentry-mcp-evals/issues/${issueId}/autofix/`, + fetch: () => HttpResponse.json({ autofix: null }), + })), { method: "post", path: "/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-42/autofix/", From 7d05d15a88d66cd03632345e10f286a9b4bd7f5b Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 17:58:33 +0000 Subject: [PATCH 2/2] test: simplify numeric issue ID regression coverage Reuse the shared event fixtures, replace the numeric autofix handler list with one route, and shorten the API guidance. Co-Authored-By: David Cramer --- docs/contributing/api-patterns.md | 6 +-- .../get-issue-details.numeric-id.test.ts | 51 +++++++------------ packages/mcp-server-mocks/src/index.ts | 17 ++----- 3 files changed, 23 insertions(+), 51 deletions(-) diff --git a/docs/contributing/api-patterns.md b/docs/contributing/api-patterns.md index b52091fa9..58e592be4 100644 --- a/docs/contributing/api-patterns.md +++ b/docs/contributing/api-patterns.md @@ -65,10 +65,8 @@ await api.issues.update({ ### Resolved Issue IDs -After resolving an issue, use `String(issue.id)` for follow-up API requests, -including events, external links, and Seer analysis. Do not switch back to -`issue.shortId`: short-ID resolution can fail for legacy mixed-case project -slugs even when numeric-ID lookup succeeds. Keep short IDs for display. +Use `String(issue.id)` for API requests after resolving an issue. Short IDs can +fail to resolve for legacy mixed-case project slugs; keep them for display. ### Multi-Region Support diff --git a/packages/mcp-core/src/tools/catalog/get-issue-details.numeric-id.test.ts b/packages/mcp-core/src/tools/catalog/get-issue-details.numeric-id.test.ts index b76b7bff3..506553756 100644 --- a/packages/mcp-core/src/tools/catalog/get-issue-details.numeric-id.test.ts +++ b/packages/mcp-core/src/tools/catalog/get-issue-details.numeric-id.test.ts @@ -19,8 +19,6 @@ const context = { userId: "1", }; -// Short IDs for legacy mixed-case project slugs can fail to resolve even though -// the numeric ID works. Reject every short-ID follow-up, including optional data. describe("issue details with an unresolvable short ID", () => { it.each([ { @@ -65,39 +63,26 @@ describe("issue details with an unresolvable short ID", () => { ), }, ])("loads $name and enrichment by numeric ID", async ({ call }) => { - const requestedIssueIds: string[] = []; const base = `https://sentry.io/api/0/organizations/${organizationSlug}`; mswServer.use( http.get(`${base}/issues/`, () => HttpResponse.json([issueFixture])), - http.get(`${base}/issues/:issueId/*`, ({ params, request }) => { - const requestedId = String(params.issueId); - requestedIssueIds.push(requestedId); - if (requestedId !== issueId) { - return HttpResponse.json( - { detail: "The requested resource does not exist" }, - { status: 404 }, - ); - } - const path = new URL(request.url).pathname; - if (path.includes("/events/")) { - return HttpResponse.json(eventsFixture); - } - if (path.endsWith("/external-issues/")) { - return HttpResponse.json([ - { - id: "123", - issueId, - serviceType: "github", - displayName: "example/app#123", - webUrl: "https://github.com/example/app/issues/123", - }, - ]); - } - if (path.endsWith("/autofix/")) { - return HttpResponse.json(autofixStateFixture); - } - return HttpResponse.json(issueFixture); - }), + http.get(`${base}/issues/${issueFixture.shortId}/*`, () => + HttpResponse.json({ detail: "Not found" }, { status: 404 }), + ), + http.get(`${base}/issues/${issueId}/autofix/`, () => + HttpResponse.json(autofixStateFixture), + ), + http.get(`${base}/issues/${issueId}/external-issues/`, () => + HttpResponse.json([ + { + id: "123", + issueId, + serviceType: "github", + displayName: "example/app#123", + webUrl: "https://github.com/example/app/issues/123", + }, + ]), + ), ); const result = await call(); @@ -106,7 +91,5 @@ describe("issue details with an unresolvable short ID", () => { expect(result).toContain(eventsFixture.id); expect(result).toContain("## Seer Analysis"); expect(result).toContain("**example/app#123** (github)"); - expect(requestedIssueIds.length).toBeGreaterThanOrEqual(3); - expect(new Set(requestedIssueIds)).toEqual(new Set([issueId])); }); }); diff --git a/packages/mcp-server-mocks/src/index.ts b/packages/mcp-server-mocks/src/index.ts index a3fc063ec..2c505f596 100644 --- a/packages/mcp-server-mocks/src/index.ts +++ b/packages/mcp-server-mocks/src/index.ts @@ -1334,20 +1334,11 @@ export const restHandlers = buildHandlers([ path: "/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/", fetch: () => HttpResponse.json({ autofix: null }), }, - // Issue details use the resolved numeric ID for enrichment requests. - ...[ - issueFixture.id, - "7890123456", // PERF-N1-001 - "123456", // DEFAULT-001 / CONTEXT-001 - "123456789", // TEAM-ISSUE-001 - "6898891101", // MCP-SERVER-EQE - "7777777777", // FUTURE-TYPE-001 - "4256774711", // BLOG-CSP-4XC - ].map((issueId) => ({ - method: "get" as const, - path: `/api/0/organizations/sentry-mcp-evals/issues/${issueId}/autofix/`, + { + method: "get", + path: "/api/0/organizations/sentry-mcp-evals/issues/:issueId(\\d+)/autofix/", fetch: () => HttpResponse.json({ autofix: null }), - })), + }, { method: "post", path: "/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-42/autofix/",