Skip to content

feat(tools): Add cursor pagination, spans resource type, and dynamic tool names#820

Open
dcramer wants to merge 7 commits intomainfrom
feat/cursor-pagination-events
Open

feat(tools): Add cursor pagination, spans resource type, and dynamic tool names#820
dcramer wants to merge 7 commits intomainfrom
feat/cursor-pagination-events

Conversation

@dcramer
Copy link
Member

@dcramer dcramer commented Mar 2, 2026

Add cursor-based pagination to event tools, a new spans resource type, and fix hardcoded tool name references that caused LLMs to call non-existent tools.

Cursor pagination for events — Both search_events and list_events had a 100-result cap with no way to page through large result sets. This adds cursor-based pagination using Sentry's Link headers (parseLinkCursor(), requestJSONWithPagination()), raises the limit max to 1000, and includes pagination metadata in formatter output. Cursor pagination was then scoped to list_events only, since search_events re-runs the LLM agent on every call making pagination wasteful and non-deterministic.

spans resource type on get_sentry_resourceget_sentry_resource(resourceType='spans', resourceId=traceId) renders the complete span tree (up to 1000 spans from the API), unlike get_trace_details which shows only ~20 selected spans. Shared trace rendering utilities (SelectedSpan, renderSpanTree, selectInterestingSpans, buildFullSpanTree) were extracted into trace-rendering.ts for reuse.

Dynamic tool name references — Tool handler outputs hardcoded references to search_events/list_events and search_issues/list_issues, but these are mutually exclusive pairs (only one is registered based on agent provider config). Added getEventsToolName() and getIssuesToolName() helpers that resolve the correct name at runtime via hasAgentProvider(). Fixed references in get-trace-details.ts, get-sentry-resource.ts, get-profile.ts, and search-issues/formatters.ts.

Both tools shared a 100-result cap with no way to page through large
result sets. This adds cursor-based pagination using Sentry's Link
headers so users can iterate through all matching events.

Key changes:
- Add parseLinkCursor() to extract next cursor from Link headers
- Add requestJSONWithPagination() in the API client
- searchEvents() now accepts cursor param and returns { body, nextCursor }
- Raise limit max from 100 to 1000 on both tools
- Add pagination section to all three formatters (errors, logs, spans)
- Mock server returns Link headers to simulate pagination

Co-Authored-By: Claude <noreply@anthropic.com>
Replace the generic "use search_events" hint with a concrete
list_events call showing the trace query, dataset, and pagination
parameters so AI assistants can page through all spans in a trace.

Co-Authored-By: Claude <noreply@anthropic.com>
@dcramer dcramer marked this pull request as ready for review March 2, 2026 19:26
search_events and list_events are mutually exclusive — only one is
registered depending on whether AI keys are configured. Since
search_events re-runs the LLM agent on every call, adding cursor
pagination to it would be wasteful and non-deterministic. Cursor
pagination only applies to list_events.

Co-Authored-By: Claude <noreply@anthropic.com>
…nces

- Add `spans` resource type to `get_sentry_resource` that renders the
  complete span tree (up to 1000 spans) for a trace, unlike
  `get_trace_details` which shows only ~20 selected spans.

- Fix hardcoded tool name references (`search_events`, `search_issues`,
  `list_events`, `list_issues`) in handler output text. These tool pairs
  are mutually exclusive based on agent provider config, so referencing
  the wrong one causes the LLM to call a non-existent tool. Added
  `getEventsToolName()` and `getIssuesToolName()` helpers that use
  `hasAgentProvider()` to resolve the correct name at runtime.

- Extract shared trace rendering utilities (`SelectedSpan`,
  `renderSpanTree`, `selectInterestingSpans`, `buildFullSpanTree`, etc.)
  into `trace-rendering.ts` to enable reuse between `get_trace_details`
  and the new `spans` resource type.

- When `get_sentry_resource` handles a `trace` type, it now suggests
  `get_sentry_resource(resourceType='spans', ...)` for the complete
  span tree instead of referencing `list_events`/`search_events`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dcramer dcramer changed the title feat(events): Add cursor pagination to search_events and list_events feat(tools): Add cursor pagination, spans resource type, and dynamic tool names Mar 2, 2026
apiService: traceApiService,
suggestSpansResource: true,
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing trace ID validation in get_sentry_resource trace case

Medium Severity

The "trace" case in get_sentry_resource handler no longer validates the trace ID format. The old code delegated to getTraceDetails.handler, which validates the ID is a 32-character hex string. The new inlined implementation omits this check, while the "spans" case (added in the same PR) correctly includes it. Invalid trace IDs now produce cryptic API errors instead of a friendly UserInputError.

Additional Locations (1)

Fix in Cursor Fix in Web

sections.push("```");
sections.push(
"Use the returned `cursor` value to fetch subsequent pages until all spans are retrieved.",
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trace output suggests wrong parameters for search_events tool

Medium Severity

When suggestSpansResource is false, formatTraceOutput suggests calling getEventsToolName() with dataset, query, sort, and cursor parameters. These parameters only exist on list_events. When hasAgentProvider() is true, the resolved name is search_events, which accepts naturalLanguageQuery instead — it has none of dataset, query, sort, or cursor. This gives LLMs an incorrect function call example in non-experimental mode with an agent provider.

Fix in Cursor Fix in Web

…ilities

Covers formatSpanDisplayName, renderSpanTree, getAllSpansFlattened,
selectInterestingSpans, and buildFullSpanTree with 37 tests including
edge cases for filtering, hierarchy, mixed span/issue arrays, and
default field handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dcramer dcramer deployed to Actions March 2, 2026 22:28 — with GitHub Actions Active
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

if (parsed.transaction) {
throw new UserInputError(
`Detected a performance summary URL for transaction "${parsed.transaction}". Use \`search_events\` to find traces and performance data for this transaction.`,
`Detected a performance summary URL for transaction "${parsed.transaction}". Use \`${getEventsToolName()}\` to find traces and performance data for this transaction.`,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spans override blocked for trace URLs

Low Severity

resolveFromParsedUrl still allows only breadcrumbs as a URL type override. After adding resourceType='spans', passing a trace URL with resourceType='spans' is rejected, so the new full-span-tree mode cannot be requested via URL-based calls.

Fix in Cursor Fix in Web

dcramer and others added 2 commits March 10, 2026 14:45
Add top-level skills that translate natural-language issue and event
searches into direct list_issues and list_events calls.

This preserves the useful query-planning behavior from the embedded
search agents while we prepare a larger overhaul that can remove the
AI search tools themselves.

Co-Authored-By: Codex <noreply@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant