Skip to content

Commit 26073ae

Browse files
dcramercodex
andauthored
feat(search-events): Add tracemetrics dataset support (#902)
Add support for Sentry's current-generation span metrics in search_events and list_events. The previous attempt in #899 targeted the wrong dataset. The current Sentry path uses dataset=tracemetrics on the events endpoint, raw metric aggregate expressions such as p95(value,http.request.duration,distribution,millisecond), and Metrics page URLs under /explore/metrics/. This updates the MCP query builder, formatting, mocks, tests, generated definitions, and docs to match that behavior. This also preserves raw aggregate sort expressions for tracemetrics so grouped metric queries keep working instead of being rewritten into Discover-style aliases. --------- Co-authored-by: Codex <codex@openai.com>
1 parent c3aa721 commit 26073ae

25 files changed

Lines changed: 1560 additions & 132 deletions

‎docs/search-events-api-patterns.md‎

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
## Overview
44

5-
The `search_events` tool provides a unified interface for searching Sentry events across different datasets (errors, logs, spans). This document covers the API patterns, query structures, and best practices for both individual event queries and aggregate queries.
5+
The `search_events` tool provides a unified interface for searching Sentry events across different datasets (`errors`, `logs`, `spans`, and `metrics`). This document covers the API patterns, query structures, and best practices for both individual event queries and aggregate queries.
66

77
## API Architecture
88

9-
### Legacy Discover API vs Modern EAP API
9+
### Discover-Style API vs Modern EAP API
1010

11-
Sentry uses two different API architectures depending on the dataset:
11+
Sentry uses two different query-building patterns depending on the dataset, even though they share the same `/events/` endpoint:
1212

13-
1. **Legacy Discover API** (errors dataset)
14-
- Uses the original Discover query format
15-
- Simpler aggregate field handling
16-
- Returns data in a different format
13+
1. **Discover-style events queries** (`errors`, `metrics`)
14+
- Use repeated `field` parameters
15+
- Use raw aggregate expressions in both `field` and `sort`
16+
- Generate Discover-style URLs for `errors` and Metrics page URLs for `metrics`
1717

18-
2. **Modern EAP (Event Analytics Platform) API** (spans, logs datasets)
19-
- Uses structured aggregate parameters
20-
- More sophisticated query capabilities
21-
- Different URL generation patterns
18+
2. **Modern EAP (Event Analytics Platform) queries** (`spans`, `logs`)
19+
- Use the same `/events/` endpoint with EAP-oriented field conventions
20+
- Support richer span/log search semantics
21+
- Generate Explore URLs for traces and logs
2222

2323
### API Endpoint
2424

@@ -29,10 +29,11 @@ All queries use the same base endpoint:
2929

3030
### Dataset Mapping
3131

32-
The tool handles dataset name mapping internally:
33-
- User specifies `errors` → API uses `errors` (Legacy Discover)
34-
- User specifies `spans` → API uses `spans` (EAP)
35-
- User specifies `logs` → API uses `ourlogs` (EAP) ⚠️ Note the transformation!
32+
The tool forwards the dataset name directly to the API:
33+
- User specifies `errors` → API uses `errors`
34+
- User specifies `spans` → API uses `spans`
35+
- User specifies `logs` → API uses `logs`
36+
- User specifies `metrics` → API uses `tracemetrics`
3637

3738
## Query Modes
3839

@@ -56,6 +57,7 @@ https://us.sentry.io/api/0/organizations/sentry/events/?dataset=spans&field=id&f
5657
- **Spans**: `id`, `span.op`, `span.description`, `span.duration`, `transaction`, `timestamp`, `project`, `trace`
5758
- **Errors**: `issue`, `title`, `project`, `timestamp`, `level`, `message`, `error.type`, `culprit`
5859
- **Logs**: `timestamp`, `project`, `message`, `severity`, `trace`
60+
- **Tracemetrics**: `id`, `timestamp`, `project`, `trace`, `metric.name`, `metric.type`, `metric.unit`, `value`
5961

6062
### 2. Aggregate Queries (Statistics)
6163

@@ -85,7 +87,7 @@ https://us.sentry.io/api/0/organizations/sentry/events/?dataset=spans&field=ai.m
8587

8688
| Parameter | Description | Example |
8789
|-----------|-------------|---------|
88-
| `dataset` | Which dataset to query | `spans`, `errors`, `logs` (API uses `ourlogs`) |
90+
| `dataset` | Which dataset to query | `spans`, `errors`, `logs`, `metrics` |
8991
| `field` | Fields to return (repeated for each field) | `field=span.op&field=count()` |
9092
| `query` | Sentry query syntax filter | `has:db.statement AND span.duration:>1000` |
9193
| `sort` | Sort order (prefix with `-` for descending) | `-timestamp`, `-count()` |
@@ -112,7 +114,13 @@ https://us.sentry.io/api/0/organizations/sentry/events/?dataset=spans&field=ai.m
112114
- Does NOT support timestamp filters in query (use `statsPeriod` instead)
113115
- Severity levels: fatal, error, warning, info, debug, trace
114116
- Common aggregate functions: `count()`, `epm()`
115-
- Uses `ourlogs` as the actual API dataset value (not `logs`)
117+
118+
#### Metrics Dataset
119+
- Represents newer span metrics, including counters, gauges, and distributions
120+
- Uses the same `/events/` endpoint with `dataset=tracemetrics`
121+
- Attribute discovery uses `/trace-items/attributes/?itemType=tracemetrics`
122+
- Aggregate fields must encode the target metric directly: `fn(value,metric.name,metric.type,metric.unit)`
123+
- Sort fields for aggregate results must stay raw, for example `-p95(value,http.request.duration,distribution,millisecond)`
116124

117125
## Query Syntax
118126

@@ -144,6 +152,12 @@ Instead of using `span.op` patterns, use `has:` queries for more flexible attrib
144152
- `max(field)` - Maximum value
145153
- `p50(field)`, `p75(field)`, `p90(field)`, `p95(field)`, `p99(field)` - Percentiles
146154

155+
#### Tracemetrics Aggregate Functions
156+
- Use the metric-aware form `fn(value,metric.name,metric.type,metric.unit)`
157+
- Counter examples: `sum(value,http.server.request.count,counter,-)`, `per_minute(value,http.server.request.count,counter,-)`
158+
- Gauge examples: `avg(value,cpu.usage,gauge,percent)`, `max(value,cpu.usage,gauge,percent)`
159+
- Distribution examples: `p75(value,http.request.duration,distribution,millisecond)`, `p95(value,http.request.duration,distribution,millisecond)`
160+
147161
#### Errors-Specific Functions
148162
- `count_if(field,equals,value)` - Conditional count
149163
- `last_seen()` - Most recent timestamp
@@ -183,6 +197,14 @@ Sort: -count()
183197
Dataset: logs
184198
```
185199

200+
### Request Duration Percentiles by Route (Trace Metrics Aggregate)
201+
```
202+
Query: metric.name:http.request.duration AND metric.type:distribution
203+
Fields: ["transaction", "p75(value,http.request.duration,distribution,millisecond)", "p95(value,http.request.duration,distribution,millisecond)", "count(value,http.request.duration,distribution,millisecond)"]
204+
Sort: -p95(value,http.request.duration,distribution,millisecond)
205+
Dataset: metrics
206+
```
207+
186208
### Tool Calls by Model (Aggregate)
187209
```
188210
Query: has:mcp.tool.name
@@ -205,7 +227,7 @@ Dataset: spans
205227
2. **Wrong sort field**: The field you sort by must be included in the fields array
206228
3. **Timestamp filters on logs**: Use `statsPeriod` parameter instead of query filters
207229
4. **Using project slugs**: API requires numeric project IDs, not slugs
208-
5. **Dataset naming**: Use `logs` in the tool, but API expects `ourlogs`
230+
5. **Tracemetrics sort mangling**: Do not rewrite `-p95(value,...)` into Discover-style aliases; the raw aggregate expression must be preserved
209231

210232
## Web UI URL Generation
211233

@@ -214,10 +236,12 @@ The tool automatically generates shareable Sentry web UI URLs after making API c
214236
- **Errors dataset**: `/organizations/{org}/discover/results/`
215237
- **Spans dataset**: `/organizations/{org}/explore/traces/`
216238
- **Logs dataset**: `/organizations/{org}/explore/logs/`
239+
- **Tracemetrics dataset**: `/organizations/{org}/explore/metrics/`
217240

218241
Note: The web UI URLs use different parameter formats than the API:
219242
- Legacy Discover uses simple field parameters
220-
- Modern Explore uses `aggregateField` with JSON-encoded values
243+
- Modern Explore uses dataset-specific parameter formats
244+
- The Metrics page uses one or more JSON-encoded `metric=` parameters
221245
- The tool handles this transformation automatically in `buildDiscoverUrl()` and `buildEapUrl()`
222246

223247
### Web URL Generation Parameters
@@ -515,4 +539,4 @@ This occurs when sorting by a field not included in the field list. Ensure your
515539
### API Errors
516540
- 400: Invalid query syntax or parameters (often due to field mismatch in aggregates)
517541
- 404: Project or organization not found
518-
- 500: Internal error (check Sentry status)
542+
- 500: Internal error (check Sentry status)

‎docs/specs/search-events.md‎

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ A unified search tool that accepts natural language queries and translates them
1616
interface SearchEventsParams {
1717
organizationSlug: string; // Required
1818
naturalLanguageQuery: string; // Natural language search description
19-
dataset?: "spans" | "errors" | "logs"; // Dataset to search (default: "errors")
19+
dataset?: "spans" | "errors" | "logs" | "metrics"; // Dataset to search (default: "errors")
2020
projectSlug?: string; // Optional - limit to specific project
2121
regionUrl?: string;
2222
limit?: number; // Default: 10, Max: 100
@@ -47,13 +47,20 @@ search_events({
4747
naturalLanguageQuery: "warning logs about memory usage",
4848
dataset: "logs"
4949
})
50+
51+
// Find request duration metrics
52+
search_events({
53+
organizationSlug: "my-org",
54+
naturalLanguageQuery: "p95 request duration by transaction this week",
55+
dataset: "metrics"
56+
})
5057
```
5158

5259
## Architecture
5360

5461
1. **Tool receives** natural language query and dataset selection
5562
2. **Fetches searchable attributes** based on dataset:
56-
- For `spans`/`logs`: Uses `/organizations/{org}/trace-items/attributes/` endpoint with parallel calls for string and number attribute types
63+
- For `spans`/`logs`/`metrics`: Uses `/organizations/{org}/trace-items/attributes/` endpoint with parallel calls for string and number attribute types
5764
- For `errors`: Uses `/organizations/{org}/tags/` endpoint (legacy, will migrate when new API supports errors)
5865
3. **OpenAI GPT-5 translates** natural language to Sentry query syntax using:
5966
- Comprehensive system prompt with Sentry query syntax rules
@@ -63,9 +70,9 @@ search_events({
6370
- Translated query string
6471
- Dataset-specific field selection
6572
- Numeric project ID (converted from slug if provided)
66-
- Proper dataset mapping (logs → ourlogs)
73+
- Public dataset normalization (`metrics` maps to the current API dataset `tracemetrics`)
6774
5. **Returns** formatted results with:
68-
- Dataset-specific rendering (console format for logs, cards for errors, timeline for spans)
75+
- Dataset-specific rendering (console format for logs, cards for errors, timeline for spans, and table/sample formatting for metrics)
6976
- Prominent rendering directives for AI agents
7077
- Shareable Sentry Explorer URL
7178

@@ -85,13 +92,16 @@ The AI produces different query patterns based on the selected dataset:
8592
- **Spans dataset**: Focus on `span.op`, `span.description`, `span.duration`, `transaction`, supports timestamp filters
8693
- **Errors dataset**: Focus on `message`, `level`, `error.type`, `error.handled`, supports timestamp filters
8794
- **Logs dataset**: Focus on `message`, `severity`, `severity_number`, **NO timestamp filters** (uses statsPeriod instead)
95+
- **Tracemetrics dataset**: Focus on `metric.name`, `metric.type`, `metric.unit`, `value`, and metric-aware aggregates like `p95(value,http.request.duration,distribution,millisecond)`
8896

8997
### Key Technical Constraints
9098

9199
- **Logs timestamp handling**: Logs don't support query-based timestamp filters like `timestamp:-1h`. Instead, use `statsPeriod=24h` parameter
92100
- **Project ID mapping**: API requires numeric project IDs, not slugs. Tool automatically converts project slugs to IDs
93-
- **Parallel attribute fetching**: For spans/logs, fetches both string and number attribute types in parallel for better performance
94-
- **itemType specification**: Must use "logs" (plural) not "log" for the trace-items attributes API
101+
- **Parallel attribute fetching**: For spans/logs/metrics, fetches both string and number attribute types in parallel for better performance
102+
- **itemType specification**: Must use `logs` and `tracemetrics` exactly for the trace-items attributes API
103+
- **Tracemetrics sort handling**: Aggregate sort expressions like `-p95(value,...)` must be sent to the API unchanged
104+
- **Tracemetrics URL generation**: Explorer links must point at `/explore/metrics/` with JSON-encoded `metric=` parameters, not the traces or logs Explore pages
95105

96106
### Tool Removal
97107

@@ -124,16 +134,17 @@ search_events({
124134
### Completed Features
125135

126136
1. **Custom attributes API integration**:
127-
- ✅ `/organizations/{org}/trace-items/attributes/` for spans/logs with parallel string/number fetching
137+
- ✅ `/organizations/{org}/trace-items/attributes/` for spans/logs/metrics with parallel string/number fetching
128138
- ✅ `/organizations/{org}/tags/` for errors (legacy API)
129139

130140
2. **Dataset mapping**:
131-
- ✅ User specifies `logs` → API uses `ourlogs`
132141
- ✅ User specifies `errors` → API uses `errors`
133142
- ✅ User specifies `spans` → API uses `spans`
143+
- ✅ User specifies `logs` → API uses `logs`
144+
- ✅ User specifies `metrics` → API uses `tracemetrics`
134145

135146
3. **URL Generation**:
136-
- ✅ Uses appropriate explore path based on dataset (`/explore/traces/`, `/explore/logs/`)
147+
- ✅ Uses appropriate explore path based on dataset (`/discover/results/`, `/explore/traces/`, `/explore/logs/`, `/explore/metrics/`)
137148
- ✅ Query and project parameters properly encoded with numeric project IDs
138149

139150
4. **Error Handling**:
@@ -146,14 +157,15 @@ search_events({
146157
- ✅ Console format for logs with severity emojis
147158
- ✅ Alert cards for errors with color-coded levels
148159
- ✅ Performance timeline for spans with duration bars
160+
- ✅ Aggregate-table and sample formatting for metrics
149161

150162
## Success Criteria - All Complete ✅
151163

152164
- ✅ **Accurate translation of common query patterns** - GPT-5 with comprehensive system prompts
153165
- ✅ **Proper handling of org-specific custom attributes** - Parallel fetching and integration
154166
- ✅ **Seamless migration from old tools** - find_errors, find_transactions removed from exports
155167
- ✅ **Maintains performance** - Parallel API calls, efficient caching, translation overhead minimal
156-
- ✅ **Supports multiple datasets** - spans, errors, logs with dataset-specific handling
168+
- ✅ **Supports multiple datasets** - spans, errors, logs, and metrics with dataset-specific handling
157169
- ✅ **Generates shareable Sentry Explorer URLs** - Proper encoding with numeric project IDs
158170
- ✅ **Clear output indicating URL should be shared** - Prominent sharing instructions
159171
- ✅ **Comprehensive test coverage** - Unit tests, integration tests, and AI evaluations
@@ -163,4 +175,4 @@ search_events({
163175

164176
- **Runtime**: OpenAI API key required (`OPENAI_API_KEY` environment variable)
165177
- **Build**: @ai-sdk/openai, ai packages added to dependencies
166-
- **Testing**: Comprehensive mocks for OpenAI and Sentry APIs
178+
- **Testing**: Comprehensive mocks for OpenAI and Sentry APIs

0 commit comments

Comments
 (0)