From daab5f5b1b3f1589cffc7b304572208d7a2f089e Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Tue, 10 Nov 2026 06:21:27 +0000 Subject: [PATCH] fix(search-issues): Report counts that match the query's event filters search_issues reported each issue's top-level count as occurrences. Sentry computes that count over the time range and the environment URL parameter only, so event filters in the query string, such as environment:production, are not applied. The issues endpoint returns those filtered counts in filtered.count and filtered.userCount (StreamGroupSerializerSnuba in getsentry/sentry). Prefer the filtered counts when Sentry returns them, so occurrences and usersImpacted match the query the caller ran. Co-Authored-By: Sergiy Dybskiy <3760543+sergical@users.noreply.github.com> --- packages/mcp-core/src/api-client/schema.ts | 9 ++++ .../support/search-issues/results.test.ts | 45 +++++++++++++++++++ .../tools/support/search-issues/results.ts | 6 ++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/packages/mcp-core/src/api-client/schema.ts b/packages/mcp-core/src/api-client/schema.ts index e2605bf25..189f5019f 100644 --- a/packages/mcp-core/src/api-client/schema.ts +++ b/packages/mcp-core/src/api-client/schema.ts @@ -853,6 +853,15 @@ export const IssueSchema = z lastSeen: z.string().datetime().nullable(), count: z.union([z.string(), z.number()]), userCount: z.union([z.string(), z.number()]), + // Counts for the events that match the query's event filters, such as + // `environment:` in the query string. Null when the query has none. + filtered: z + .object({ + count: z.union([z.string(), z.number()]), + userCount: z.union([z.string(), z.number()]), + }) + .nullable() + .optional(), permalink: z.string().url(), project: ProjectSchema, platform: z.string().nullable().optional(), diff --git a/packages/mcp-core/src/tools/support/search-issues/results.test.ts b/packages/mcp-core/src/tools/support/search-issues/results.test.ts index 549bb3802..23779b7cd 100644 --- a/packages/mcp-core/src/tools/support/search-issues/results.test.ts +++ b/packages/mcp-core/src/tools/support/search-issues/results.test.ts @@ -167,6 +167,51 @@ describe("buildIssueResults", () => { `); }); + it("reports the counts that match the query's event filters", () => { + // Sentry's top-level count ignores event filters in the query string, + // such as `environment:`; `filtered` applies them. + const structuredContent = build({ + inputQuery: "is:unresolved environment:production", + query: "is:unresolved environment:production", + issues: [ + IssueSchema.parse( + createPerformanceIssue({ + shortId: "TEST-FILTERED", + count: "300", + userCount: 9, + filtered: { + count: "120", + userCount: 5, + firstSeen: "2025-01-01T00:00:00Z", + lastSeen: "2025-01-02T00:00:00Z", + }, + } as Parameters[0]), + ), + IssueSchema.parse( + createPerformanceIssue({ + shortId: "TEST-UNFILTERED", + count: "100", + userCount: 10, + filtered: null, + } as Parameters[0]), + ), + ], + }); + + expect( + structuredContent.issues.map( + ({ shortId, occurrences, usersImpacted }) => ({ + shortId, + occurrences, + usersImpacted, + }), + ), + ).toEqual([ + { shortId: "TEST-FILTERED", occurrences: 120, usersImpacted: 5 }, + { shortId: "TEST-UNFILTERED", occurrences: 100, usersImpacted: 10 }, + ]); + }); + it("only reads the reporter from feedback issues", () => { const structuredContent = build({ issues: [ diff --git a/packages/mcp-core/src/tools/support/search-issues/results.ts b/packages/mcp-core/src/tools/support/search-issues/results.ts index 8ae5eeab3..49269f9d4 100644 --- a/packages/mcp-core/src/tools/support/search-issues/results.ts +++ b/packages/mcp-core/src/tools/support/search-issues/results.ts @@ -145,8 +145,10 @@ export function buildIssueResults( status: issue.status, substatus: issue.substatus ?? null, issueCategory: issue.issueCategory ?? null, - occurrences: Number(issue.count) || 0, - usersImpacted: Number(issue.userCount) || 0, + // `count` ignores event filters in the query, such as `environment:`; + // `filtered` applies them. + occurrences: Number(issue.filtered?.count ?? issue.count) || 0, + usersImpacted: Number(issue.filtered?.userCount ?? issue.userCount) || 0, assignedTo: formatAssignee(issue.assignedTo), assigneeEmail: formatAssigneeEmail(issue.assignedTo), reporter: formatReporter(issue),