Repository navigation
fix(mcp-core): Allow string values in events-timeseries response - #1424
sentry[bot] wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d6bfafc. Configure here.
Date-typed aggregates such as max(timestamp) return ISO datetime strings from events-stats, while empty buckets are zero-filled with 0. Coercing the strings to 0 made Peak report an empty bucket. Order them by parsed time instead, and cover the zero-filled production shape.
There was a problem hiding this comment.
I checked the motivating failure and the change is justified. I pushed one correction in 6ede0d4.
Evidence: The only MCP-SERVER-GCQ event came from search_events on the errors dataset. It requested events-stats with yAxis=max(timestamp), and the ZodError reports expected number, received string only for buckets that had data (indices 6–12 and 22–24). That matches Sentry's producer: SnubaTSResultSerializer emits {"count": r.get(column, 0)} without type coercion, and zerofill writes a numeric 0 into empty buckets. So for date-typed aggregates, count is legitimately a datetime string next to numeric zeros. Widening the schema to string | number is the right-sized fix. The response is still validated as a tuple of buckets.
Correction: The formatter coerced datetime strings to 0. With the real response shape, every value was 0, so Peak pointed at the first empty bucket (Peak: 0 at …), not the latest timestamp. Datetime strings are now ordered by Date.parse and still displayed as the raw string. Numeric and additive handling is unchanged. I also updated the ISO test to include a zero-filled bucket and +00:00 offsets, and to assert the peak. Without the formatter change, that test fails with Peak: 0. I also applied oxfmt to the schema line.
Verification: mcp-core typecheck passes, and so does the full mcp-core test suite (1755 passed, 6 skipped). Repo lint passes and the touched files pass the oxfmt check. Reverting the schema change makes both new tests fail.
main (#1431) moved search_events time series from events-stats to events-timeseries and removed EventsStatsResponseSchema. The new endpoint still passes aggregate values through unchanged (`value=row.get(axis, 0)`), so date-typed aggregates like max(timestamp) still return ISO strings. Accept string values in EventsTimeSeriesValueSchema, keep the parsed-time ordering for Peak, and update the regression tests to the events-timeseries shape.
There was a problem hiding this comment.
Updated the title to match the current endpoint. I also merged main and ported the fix to its events-timeseries response shape (cba5c646).
The MCP-SERVER-GCQ failure came from max(timestamp) values being strings. The backend's OrganizationEventsTimeseriesEndpoint.serialize_timeseries passes row.get(axis, 0) through as value, so switching from /events-stats/ to /events-timeseries/ did not remove the underlying string-value case. The current EventsTimeSeriesValueSchema only accepted numbers and would reject those values.
The updated fix accepts string values, preserves main's incomplete-bucket handling, sorts date strings by parsed time for Peak, and displays the original value. I updated the regression tests to use the new endpoint response shape. Locally, the full mcp-core suite passes (1773 passed, 6 skipped), as do tsc, changed-file oxlint/oxfmt, and ast-grep lint. Both regression tests fail when either the schema or formatter fix is reverted.
CI on cba5c646 still needs to pass before I can report this ready for human merge.
There was a problem hiding this comment.
Looks good to me at cba5c646. I rechecked the premise against Sentry's source. For the errors dataset, OrganizationEventsTimeseriesEndpoint calls the same dataset.timeseries_query path as events-stats. serialize_timeseries then builds each bucket with value=row.get(axis, 0) and never converts it. Row.value: float is only a type hint, so the string max(timestamp) values behind MCP-SERVER-GCQ still reach EventsTimeSeriesValueSchema. Widening the type to number | string is the narrow fix, and number values behave the same as before. max(...) isn't additive, so the date values never feed into Total.
Locally, all 275 search-events and api-client tests pass. If I put back the old z.number() schema, both new regression tests fail. All 33 CI checks pass on this head, including the required test. The one review thread (Cursor's NaN note) is outdated and resolved.
Separately, older self-hosted versions don't have /events-timeseries/ or its current response shape. That comes from #1431, not this PR, so it doesn't block this one.

This PR addresses a
ZodErroroccurring when the Sentry/events-stats/API returns thecountfield as a string, particularly for non-additive aggregates likemax(timestamp).Root Cause:
The
EventsStatsResponseSchemainpackages/mcp-core/src/api-client/schema.tswas expectingcountto always be a number (z.number()), but the Sentry API can return it as a string (e.g., an epoch timestamp string or an ISO datetime string).Changes Made:
EventsStatsResponseSchemainpackages/mcp-core/src/api-client/schema.tsto define thecountfield asz.union([z.string(), z.number()]). This allows the schema to correctly parse both numeric and string representations ofcount.formatTimeSeriesResultsfunction inpackages/mcp-core/src/tools/support/search-events/formatters.ts.valueused for calculations (e.g., total, peak comparison) is now derived by coercing the rawcountto a number usingNumber(). If coercion results inNaN(e.g., for ISO datetime strings),valuedefaults to0for arithmetic safety.displayproperty was introduced for each data point. This property holds the raw string value if it's not a valid number, or the localized numeric string otherwise. This ensures that non-numeric values (like ISO datetimes) are displayed correctly to the user instead ofNaN.displayproperty.packages/mcp-core/src/tools/catalog/search-events.test.tsto specifically cover the scenario where the API returns ISO datetime strings forcount. This test asserts that the output correctly includes the datetime string and does not containNaN.These changes ensure robust handling of varying
countdata types from the Sentry API, preventing validation errors and improving the display of time series results.Fixes MCP-SERVER-GCQ
@sentry <feedback>: Autofix iterates on these changes@sentry stop iterating: Autofix stops iterating on this runThis PR was automatically generated by Sentry. You can adjust this setting at any time.