Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/mcp-core/src/tools/support/search-events/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,8 @@ export function createDatasetAttributesTool(options: {
attributeTypes,
substringMatch,
query,
// Always call attributes endpoint with context
context: true,
},
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import { StructuredOutputScorer } from "./utils/structuredOutputScorer";
import "../setup-env";

// Compares the attribute the agent picks when the trace-items attributes
// endpoint is called with and without `expand=context`. The with-context span
// endpoint is called with and without `expand=context`. The with-context
// fixtures list the replacement for each deprecated attribute (e.g.
// user.geo.region for geo.region), so only that path should use it.
//
// The datasetAttributes tool does not request context itself, so this service
// forces it on or off for every attributes request.
// This service forces the context on or off for every attributes request.
class AttributeContextApiService extends SentryApiService {
constructor(private readonly attributeContext: boolean) {
super({ accessToken: "test-token" });
Expand Down Expand Up @@ -130,6 +129,90 @@ describeEval("search-events-agent-attributes-without-context", {
!value.includes("kiosk.firmware"),
},
},
{
// Without context the logs attributes only list code.filepath
input:
"Count error logs grouped by source code file path over the last 24 hours",
expectedTools: [
{
name: "datasetAttributes",
arguments: {
dataset: "logs",
},
},
],
expected: {
dataset: "logs",
fields: (value: unknown) =>
Array.isArray(value) &&
value.includes("code.filepath") &&
!value.includes("code.file.path"),
},
},
{
// Without context the metrics attributes only list http.method
input:
"In metrics, show p95 http.request.duration grouped by HTTP method over the last 24 hours",
expectedTools: [
{
name: "datasetAttributes",
arguments: {
dataset: "metrics",
},
},
],
expected: {
dataset: "metrics",
fields: (value: unknown) =>
Array.isArray(value) &&
value.includes("http.method") &&
!value.includes("http.request.method"),
},
},
{
// worker.queue_wait and worker.queue_wait_time are made-up attributes.
// Without briefs the agent can only match on the name, and
// worker.queue_wait_time matches "worker queue wait time"
input:
"From logs, what is the average worker queue wait time jobs actually experienced over the last 24 hours?",
expectedTools: [
{
name: "datasetAttributes",
arguments: {
dataset: "logs",
},
},
],
expected: {
dataset: "logs",
fields: (value: unknown) =>
Array.isArray(value) &&
value.includes("avg(worker.queue_wait_time)") &&
!value.includes("avg(worker.queue_wait)"),
},
},
{
// fleet.region and fleet.deployment_region are made-up attributes.
// Without briefs the agent can only match on the name, and
// fleet.deployment_region matches "fleet deployment region"
input:
"In metrics, show p95 http.request.duration grouped by the fleet deployment region each server is currently running in over the last 24 hours",
expectedTools: [
{
name: "datasetAttributes",
arguments: {
dataset: "metrics",
},
},
],
expected: {
dataset: "metrics",
fields: (value: unknown) =>
Array.isArray(value) &&
value.includes("fleet.deployment_region") &&
!value.includes("fleet.region"),
},
},
];
},
task: async (input) => {
Expand Down Expand Up @@ -294,6 +377,91 @@ describeEval("search-events-agent-attributes-with-context", {
!value.includes("kiosk.firmware_revision"),
},
},
{
// Context marks code.filepath as deprecated in favor of code.file.path
input:
"Count error logs grouped by source code file path over the last 24 hours",
expectedTools: [
{
name: "datasetAttributes",
arguments: {
dataset: "logs",
},
},
],
expected: {
dataset: "logs",
fields: (value: unknown) =>
Array.isArray(value) &&
value.includes("code.file.path") &&
!value.includes("code.filepath"),
},
},
{
// Context marks http.method as deprecated in favor of
// http.request.method
input:
"In metrics, show p95 http.request.duration grouped by HTTP method over the last 24 hours",
expectedTools: [
{
name: "datasetAttributes",
arguments: {
dataset: "metrics",
},
},
],
expected: {
dataset: "metrics",
fields: (value: unknown) =>
Array.isArray(value) &&
value.includes("http.request.method") &&
!value.includes("http.method"),
},
},
{
// worker.queue_wait_time matches "worker queue wait time" by name, but
// its brief says it is a configured limit. Only worker.queue_wait's
// brief describes the measured wait
input:
"From logs, what is the average worker queue wait time jobs actually experienced over the last 24 hours?",
expectedTools: [
{
name: "datasetAttributes",
arguments: {
dataset: "logs",
},
},
],
expected: {
dataset: "logs",
fields: (value: unknown) =>
Array.isArray(value) &&
value.includes("avg(worker.queue_wait)") &&
!value.includes("avg(worker.queue_wait_time)"),
},
},
{
// fleet.deployment_region matches "fleet deployment region" by name,
// but its brief says it is the region the server moves to next. Only
// fleet.region's brief describes where the server runs now
input:
"In metrics, show p95 http.request.duration grouped by the fleet deployment region each server is currently running in over the last 24 hours",
expectedTools: [
{
name: "datasetAttributes",
arguments: {
dataset: "metrics",
},
},
],
expected: {
dataset: "metrics",
fields: (value: unknown) =>
Array.isArray(value) &&
value.includes("fleet.region") &&
!value.includes("fleet.deployment_region"),
},
},
];
},
task: async (input) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describeEval("search-events-agent", {
query: (value: unknown) =>
typeof value === "string" &&
[
'transaction:"VPN connections"',
'sentry.segment.name:"VPN connections"',
"tags[type]:Unified",
"tags[country]:CN",
].every((token) => value.includes(token)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
[
{
"key": "severity_number",
"name": "severity_number",
"context": {
"isConvention": false,
"brief": "The numeric severity level of the log.",
"isDeprecated": false,
"examples": [17]
}
},
{
"key": "sentry.observed_timestamp_nanos",
"name": "sentry.observed_timestamp_nanos",
"context": {
"isConvention": false,
"brief": "When Sentry received the log, in nanoseconds since the Unix epoch.",
"isDeprecated": false
}
},
{
"key": "timestamp",
"name": "timestamp",
"context": {
"isConvention": false,
"brief": "When the log was emitted.",
"isDeprecated": false
}
},
{
"key": "custom.duration",
"name": "custom.duration",
"context": {}
},
{
"key": "custom.bytes",
"name": "custom.bytes",
"context": {}
},
{
"key": "worker.queue_wait",
"name": "worker.queue_wait",
"context": {
"isConvention": true,
"brief": "How long the job waited in the worker queue before a worker picked it up, in milliseconds.",
"isDeprecated": false,
"examples": [412]
}
},
{
"key": "worker.queue_wait_time",
"name": "worker.queue_wait_time",
"context": {
"isConvention": true,
"brief": "The maximum time a job may wait in the worker queue before it is dropped, as configured on the worker. It is a limit, not a measured wait.",
"isDeprecated": false,
"examples": [30000]
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,13 @@
{
"key": "custom.bytes",
"name": "Custom Bytes"
},
{
"key": "worker.queue_wait",
"name": "worker.queue_wait"
},
{
"key": "worker.queue_wait_time",
"name": "worker.queue_wait_time"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
[
{
"key": "message",
"name": "message",
"context": {
"isConvention": false,
"brief": "The body of the log message.",
"isDeprecated": false
}
},
{
"key": "severity",
"name": "severity",
"context": {
"isConvention": false,
"brief": "The severity level of the log (trace, debug, info, warn, error, fatal).",
"isDeprecated": false,
"examples": ["error"]
}
},
{
"key": "sentry.item_id",
"name": "sentry.item_id",
"context": {
"isConvention": false,
"brief": "The unique ID of the log item.",
"isDeprecated": false
}
},
{
"key": "project",
"name": "project",
"context": {
"isConvention": false,
"brief": "The name of the project. In some pages of sentry.io, you can also filter on project using a dropdown.",
"isDeprecated": false
}
},
{
"key": "environment",
"name": "environment",
"context": {
"isConvention": true,
"brief": "The sentry environment.",
"isDeprecated": true,
"examples": ["production"],
"replacementAttribute": "sentry.environment"
}
},
{
"key": "release",
"name": "release",
"context": {
"isConvention": true,
"brief": "The sentry release.",
"isDeprecated": true,
"examples": ["production"],
"replacementAttribute": "sentry.release"
}
},
{
"key": "trace",
"name": "trace",
"context": {
"isConvention": false,
"brief": "The ID of the trace the log was emitted in.",
"isDeprecated": false
}
},
{
"key": "level",
"name": "level",
"context": {}
},
{
"key": "logger",
"name": "logger",
"context": {}
},
{
"key": "module",
"name": "module",
"context": {}
},
{
"key": "custom.service",
"name": "custom.service",
"context": {}
},
{
"key": "custom.component",
"name": "custom.component",
"context": {}
},
{
"key": "code.filepath",
"name": "code.filepath",
"context": {
"isConvention": true,
"brief": "The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path).",
"isDeprecated": true,
"examples": ["/app/myapplication/http/handler/server.py"],
"replacementAttribute": "code.file.path"
}
}
]
Loading
Loading