diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index 3283d4bb2..ab84ffb7b 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -304,7 +304,7 @@ Some tools (`search_events` and `search_issues`) implement a two-tier agent patt ``` 1. User: "Show me errors from yesterday" ↓ -2. Claude: Calls search_events(query="errors from yesterday") +2. Claude: Calls search_events(dataset="errors", query="errors from yesterday") ↓ 3. MCP Tool Handler: Receives request ↓ diff --git a/docs/specs/search-events.md b/docs/specs/search-events.md index 6a9868c13..34a93ed98 100644 --- a/docs/specs/search-events.md +++ b/docs/specs/search-events.md @@ -16,7 +16,7 @@ A unified search tool that accepts natural language queries and translates them interface SearchEventsParams { organizationSlug: string; // Required query: string; // Natural language search description - dataset?: "spans" | "errors" | "logs" | "metrics"; // Dataset to search (default: "errors") + dataset: "spans" | "errors" | "logs" | "metrics" | "profiles" | "replays"; // Required; the agent may correct it projectSlug?: string; // Optional - limit to specific project regionUrl?: string; limit?: number; // Default: 10, Max: 100 @@ -27,9 +27,10 @@ interface SearchEventsParams { ### Examples ```typescript -// Find errors (errors dataset is default) +// Find errors search_events({ organizationSlug: "my-org", + dataset: "errors", query: "database timeouts in checkout flow from last hour" }) @@ -152,6 +153,7 @@ find_errors({ // After search_events({ organizationSlug: "sentry", + dataset: "errors", query: "unresolved errors in checkout.js" }) ``` diff --git a/docs/testing/stdio.md b/docs/testing/stdio.md index 8a1aa0076..1c6e51ea3 100644 --- a/docs/testing/stdio.md +++ b/docs/testing/stdio.md @@ -198,7 +198,7 @@ This opens the MCP Inspector at `http://localhost:6274` 1. **List Tools** - Verify expected tools appear 2. **Call a tool** - Start with `execute_sentry_tool` using `name="whoami"` and `arguments={}` 3. **Test with parameters** - Try `find_organizations()` -4. **Test complex operations** - Try `search_events(query="errors in the last hour")` +4. **Test complex operations** - Try `search_events(dataset="errors", query="errors in the last hour")` **Example test sequence:** ``` @@ -207,6 +207,7 @@ This opens the MCP Inspector at `http://localhost:6274` 3. find_projects(organizationSlug="your-org") 4. search_events( organizationSlug="your-org", + dataset="errors", query="errors from yesterday" ) ``` @@ -483,7 +484,7 @@ OPENAI_API_KEY=your-key pnpm start --access-token=TOKEN # Test search_events and search_issues work # In MCP Inspector: -# - Call search_events(query="errors in production") +# - Call search_events(dataset="errors", query="errors in production") # - Call search_issues(query="unresolved crashes") ``` diff --git a/packages/mcp-core/src/skillDefinitions.json b/packages/mcp-core/src/skillDefinitions.json index 11cba76f5..216ff0272 100644 --- a/packages/mcp-core/src/skillDefinitions.json +++ b/packages/mcp-core/src/skillDefinitions.json @@ -194,7 +194,7 @@ }, { "name": "search_events", - "description": "Search Sentry events and replays. Use for event counts/statistics.\n\n`query` is natural language or Sentry search syntax; a configured agent fixes dataset, query, fields, and sort.\n\nSupports THREE query types:\n1. AGGREGATIONS (counts, sums, averages): 'how many errors', 'total tokens'\n2. Individual events with timestamps: 'error logs from last hour'\n3. TIME SERIES (metric over time): 'errors per hour', 'error trend over time'\n\nDatasets:\n- errors: Exception/crash events with stack traces, usually grouped into issues\n- logs: Application log entries, including error-severity log messages\n- spans: Raw trace/span events for performance, AI/LLM calls, requests, and operations\n- metrics: Metric rows and aggregates: counters, gauges, distributions, values\n- profiles: Transaction/continuous profile results, profile IDs, profiled transactions\n- replays: Session replay results: rage clicks, dead clicks, visited pages, replay users\nIf the user says logs, log messages, error logs, or warning logs, choose logs instead of errors.\n\nReplay searches return replay lists only; replay count()/avg()/sum() are not supported.\n\nNOT for grouped issue lists (use search_issues) or app screenshots/images (use get_latest_base_snapshot).\n\n\nsearch_events(organizationSlug='my-org', dataset='errors', query='how many errors today')\nsearch_events(organizationSlug='my-org', dataset='errors', fields=['issue', 'count()'], sort='-count()')\nsearch_events(organizationSlug='my-org', dataset='errors', query='errors per hour last 24h')\nsearch_events(organizationSlug='my-org', dataset='spans', query='span.op:db', sort='-span.duration')\nsearch_events(organizationSlug='my-org', dataset='replays', query='count_errors:>0', sort='-count_errors')\n\n\n\n- name/otherName notation means /; parse it directly, don't call find_organizations/find_projects.\n- Use fields with aggregate functions like count(), avg(), sum() for statistics\n- Sort by -count() for most common, -timestamp for newest\n", + "description": "Search Sentry events and replays. Use for event counts/statistics.\n\n`query` is natural language or Sentry search syntax; a configured agent fixes dataset, query, fields, and sort.\n\nSupports THREE query types:\n1. AGGREGATIONS (counts, sums, averages): 'how many errors', 'total tokens'\n2. Individual events with timestamps: 'error logs from last hour'\n3. TIME SERIES (metric over time): 'errors per hour', 'error trend over time'\n\nDatasets:\n- errors: Exception/crash events with stack traces, usually grouped into issues\n- logs: Application log entries, including error-severity log messages\n- spans: Raw trace/span events for performance, AI/LLM calls, requests, and operations\n- metrics: Metric rows and aggregates: counters, gauges, distributions, values\n- profiles: Transaction/continuous profile results, profile IDs, profiled transactions\n- replays: Session replay results: rage clicks, dead clicks, visited pages, replay users\nPick logs for log messages (incl. error/warning logs); spans for API/HTTP calls, DB queries, latency.\n\nReplay searches return replay lists only; replay count()/avg()/sum() are not supported.\n\nNOT for grouped issue lists (use search_issues) or app screenshots/images (use get_latest_base_snapshot).\n\n\nsearch_events(organizationSlug='my-org', dataset='errors', query='how many errors today')\nsearch_events(organizationSlug='my-org', dataset='errors', fields=['issue', 'count()'], sort='-count()')\nsearch_events(organizationSlug='my-org', dataset='errors', query='errors per hour last 24h')\nsearch_events(organizationSlug='my-org', dataset='spans', query='span.op:db', sort='-span.duration')\nsearch_events(organizationSlug='my-org', dataset='replays', query='count_errors:>0', sort='-count_errors')\n\n\n\n- name/otherName notation means /; parse it directly, don't call find_organizations/find_projects.\n- Use fields with aggregate functions like count(), avg(), sum() for statistics\n- Sort by -count() for most common, -timestamp for newest\n", "requiredScopes": ["event:read"] }, { @@ -269,7 +269,7 @@ }, { "name": "search_events", - "description": "Search Sentry events and replays. Use for event counts/statistics.\n\n`query` is natural language or Sentry search syntax; a configured agent fixes dataset, query, fields, and sort.\n\nSupports THREE query types:\n1. AGGREGATIONS (counts, sums, averages): 'how many errors', 'total tokens'\n2. Individual events with timestamps: 'error logs from last hour'\n3. TIME SERIES (metric over time): 'errors per hour', 'error trend over time'\n\nDatasets:\n- errors: Exception/crash events with stack traces, usually grouped into issues\n- logs: Application log entries, including error-severity log messages\n- spans: Raw trace/span events for performance, AI/LLM calls, requests, and operations\n- metrics: Metric rows and aggregates: counters, gauges, distributions, values\n- profiles: Transaction/continuous profile results, profile IDs, profiled transactions\n- replays: Session replay results: rage clicks, dead clicks, visited pages, replay users\nIf the user says logs, log messages, error logs, or warning logs, choose logs instead of errors.\n\nReplay searches return replay lists only; replay count()/avg()/sum() are not supported.\n\nNOT for grouped issue lists (use search_issues) or app screenshots/images (use get_latest_base_snapshot).\n\n\nsearch_events(organizationSlug='my-org', dataset='errors', query='how many errors today')\nsearch_events(organizationSlug='my-org', dataset='errors', fields=['issue', 'count()'], sort='-count()')\nsearch_events(organizationSlug='my-org', dataset='errors', query='errors per hour last 24h')\nsearch_events(organizationSlug='my-org', dataset='spans', query='span.op:db', sort='-span.duration')\nsearch_events(organizationSlug='my-org', dataset='replays', query='count_errors:>0', sort='-count_errors')\n\n\n\n- name/otherName notation means /; parse it directly, don't call find_organizations/find_projects.\n- Use fields with aggregate functions like count(), avg(), sum() for statistics\n- Sort by -count() for most common, -timestamp for newest\n", + "description": "Search Sentry events and replays. Use for event counts/statistics.\n\n`query` is natural language or Sentry search syntax; a configured agent fixes dataset, query, fields, and sort.\n\nSupports THREE query types:\n1. AGGREGATIONS (counts, sums, averages): 'how many errors', 'total tokens'\n2. Individual events with timestamps: 'error logs from last hour'\n3. TIME SERIES (metric over time): 'errors per hour', 'error trend over time'\n\nDatasets:\n- errors: Exception/crash events with stack traces, usually grouped into issues\n- logs: Application log entries, including error-severity log messages\n- spans: Raw trace/span events for performance, AI/LLM calls, requests, and operations\n- metrics: Metric rows and aggregates: counters, gauges, distributions, values\n- profiles: Transaction/continuous profile results, profile IDs, profiled transactions\n- replays: Session replay results: rage clicks, dead clicks, visited pages, replay users\nPick logs for log messages (incl. error/warning logs); spans for API/HTTP calls, DB queries, latency.\n\nReplay searches return replay lists only; replay count()/avg()/sum() are not supported.\n\nNOT for grouped issue lists (use search_issues) or app screenshots/images (use get_latest_base_snapshot).\n\n\nsearch_events(organizationSlug='my-org', dataset='errors', query='how many errors today')\nsearch_events(organizationSlug='my-org', dataset='errors', fields=['issue', 'count()'], sort='-count()')\nsearch_events(organizationSlug='my-org', dataset='errors', query='errors per hour last 24h')\nsearch_events(organizationSlug='my-org', dataset='spans', query='span.op:db', sort='-span.duration')\nsearch_events(organizationSlug='my-org', dataset='replays', query='count_errors:>0', sort='-count_errors')\n\n\n\n- name/otherName notation means /; parse it directly, don't call find_organizations/find_projects.\n- Use fields with aggregate functions like count(), avg(), sum() for statistics\n- Sort by -count() for most common, -timestamp for newest\n", "requiredScopes": ["event:read"] }, { @@ -405,7 +405,7 @@ }, { "name": "search_events", - "description": "Search Sentry events and replays. Use for event counts/statistics.\n\n`query` is natural language or Sentry search syntax; a configured agent fixes dataset, query, fields, and sort.\n\nSupports THREE query types:\n1. AGGREGATIONS (counts, sums, averages): 'how many errors', 'total tokens'\n2. Individual events with timestamps: 'error logs from last hour'\n3. TIME SERIES (metric over time): 'errors per hour', 'error trend over time'\n\nDatasets:\n- errors: Exception/crash events with stack traces, usually grouped into issues\n- logs: Application log entries, including error-severity log messages\n- spans: Raw trace/span events for performance, AI/LLM calls, requests, and operations\n- metrics: Metric rows and aggregates: counters, gauges, distributions, values\n- profiles: Transaction/continuous profile results, profile IDs, profiled transactions\n- replays: Session replay results: rage clicks, dead clicks, visited pages, replay users\nIf the user says logs, log messages, error logs, or warning logs, choose logs instead of errors.\n\nReplay searches return replay lists only; replay count()/avg()/sum() are not supported.\n\nNOT for grouped issue lists (use search_issues) or app screenshots/images (use get_latest_base_snapshot).\n\n\nsearch_events(organizationSlug='my-org', dataset='errors', query='how many errors today')\nsearch_events(organizationSlug='my-org', dataset='errors', fields=['issue', 'count()'], sort='-count()')\nsearch_events(organizationSlug='my-org', dataset='errors', query='errors per hour last 24h')\nsearch_events(organizationSlug='my-org', dataset='spans', query='span.op:db', sort='-span.duration')\nsearch_events(organizationSlug='my-org', dataset='replays', query='count_errors:>0', sort='-count_errors')\n\n\n\n- name/otherName notation means /; parse it directly, don't call find_organizations/find_projects.\n- Use fields with aggregate functions like count(), avg(), sum() for statistics\n- Sort by -count() for most common, -timestamp for newest\n", + "description": "Search Sentry events and replays. Use for event counts/statistics.\n\n`query` is natural language or Sentry search syntax; a configured agent fixes dataset, query, fields, and sort.\n\nSupports THREE query types:\n1. AGGREGATIONS (counts, sums, averages): 'how many errors', 'total tokens'\n2. Individual events with timestamps: 'error logs from last hour'\n3. TIME SERIES (metric over time): 'errors per hour', 'error trend over time'\n\nDatasets:\n- errors: Exception/crash events with stack traces, usually grouped into issues\n- logs: Application log entries, including error-severity log messages\n- spans: Raw trace/span events for performance, AI/LLM calls, requests, and operations\n- metrics: Metric rows and aggregates: counters, gauges, distributions, values\n- profiles: Transaction/continuous profile results, profile IDs, profiled transactions\n- replays: Session replay results: rage clicks, dead clicks, visited pages, replay users\nPick logs for log messages (incl. error/warning logs); spans for API/HTTP calls, DB queries, latency.\n\nReplay searches return replay lists only; replay count()/avg()/sum() are not supported.\n\nNOT for grouped issue lists (use search_issues) or app screenshots/images (use get_latest_base_snapshot).\n\n\nsearch_events(organizationSlug='my-org', dataset='errors', query='how many errors today')\nsearch_events(organizationSlug='my-org', dataset='errors', fields=['issue', 'count()'], sort='-count()')\nsearch_events(organizationSlug='my-org', dataset='errors', query='errors per hour last 24h')\nsearch_events(organizationSlug='my-org', dataset='spans', query='span.op:db', sort='-span.duration')\nsearch_events(organizationSlug='my-org', dataset='replays', query='count_errors:>0', sort='-count_errors')\n\n\n\n- name/otherName notation means /; parse it directly, don't call find_organizations/find_projects.\n- Use fields with aggregate functions like count(), avg(), sum() for statistics\n- Sort by -count() for most common, -timestamp for newest\n", "requiredScopes": ["event:read"] }, { diff --git a/packages/mcp-core/src/toolDefinitions.json b/packages/mcp-core/src/toolDefinitions.json index 534c64d3c..67159e0fc 100644 --- a/packages/mcp-core/src/toolDefinitions.json +++ b/packages/mcp-core/src/toolDefinitions.json @@ -7203,7 +7203,7 @@ }, { "name": "search_events", - "description": "Search Sentry events and replays. Use for event counts/statistics.\n\n`query` is natural language or Sentry search syntax; a configured agent fixes dataset, query, fields, and sort.\n\nSupports THREE query types:\n1. AGGREGATIONS (counts, sums, averages): 'how many errors', 'total tokens'\n2. Individual events with timestamps: 'error logs from last hour'\n3. TIME SERIES (metric over time): 'errors per hour', 'error trend over time'\n\nDatasets:\n- errors: Exception/crash events with stack traces, usually grouped into issues\n- logs: Application log entries, including error-severity log messages\n- spans: Raw trace/span events for performance, AI/LLM calls, requests, and operations\n- metrics: Metric rows and aggregates: counters, gauges, distributions, values\n- profiles: Transaction/continuous profile results, profile IDs, profiled transactions\n- replays: Session replay results: rage clicks, dead clicks, visited pages, replay users\nIf the user says logs, log messages, error logs, or warning logs, choose logs instead of errors.\n\nReplay searches return replay lists only; replay count()/avg()/sum() are not supported.\n\nNOT for grouped issue lists (use search_issues) or app screenshots/images (use get_latest_base_snapshot).\n\n\nsearch_events(organizationSlug='my-org', dataset='errors', query='how many errors today')\nsearch_events(organizationSlug='my-org', dataset='errors', fields=['issue', 'count()'], sort='-count()')\nsearch_events(organizationSlug='my-org', dataset='errors', query='errors per hour last 24h')\nsearch_events(organizationSlug='my-org', dataset='spans', query='span.op:db', sort='-span.duration')\nsearch_events(organizationSlug='my-org', dataset='replays', query='count_errors:>0', sort='-count_errors')\n\n\n\n- name/otherName notation means /; parse it directly, don't call find_organizations/find_projects.\n- Use fields with aggregate functions like count(), avg(), sum() for statistics\n- Sort by -count() for most common, -timestamp for newest\n", + "description": "Search Sentry events and replays. Use for event counts/statistics.\n\n`query` is natural language or Sentry search syntax; a configured agent fixes dataset, query, fields, and sort.\n\nSupports THREE query types:\n1. AGGREGATIONS (counts, sums, averages): 'how many errors', 'total tokens'\n2. Individual events with timestamps: 'error logs from last hour'\n3. TIME SERIES (metric over time): 'errors per hour', 'error trend over time'\n\nDatasets:\n- errors: Exception/crash events with stack traces, usually grouped into issues\n- logs: Application log entries, including error-severity log messages\n- spans: Raw trace/span events for performance, AI/LLM calls, requests, and operations\n- metrics: Metric rows and aggregates: counters, gauges, distributions, values\n- profiles: Transaction/continuous profile results, profile IDs, profiled transactions\n- replays: Session replay results: rage clicks, dead clicks, visited pages, replay users\nPick logs for log messages (incl. error/warning logs); spans for API/HTTP calls, DB queries, latency.\n\nReplay searches return replay lists only; replay count()/avg()/sum() are not supported.\n\nNOT for grouped issue lists (use search_issues) or app screenshots/images (use get_latest_base_snapshot).\n\n\nsearch_events(organizationSlug='my-org', dataset='errors', query='how many errors today')\nsearch_events(organizationSlug='my-org', dataset='errors', fields=['issue', 'count()'], sort='-count()')\nsearch_events(organizationSlug='my-org', dataset='errors', query='errors per hour last 24h')\nsearch_events(organizationSlug='my-org', dataset='spans', query='span.op:db', sort='-span.duration')\nsearch_events(organizationSlug='my-org', dataset='replays', query='count_errors:>0', sort='-count_errors')\n\n\n\n- name/otherName notation means /; parse it directly, don't call find_organizations/find_projects.\n- Use fields with aggregate functions like count(), avg(), sum() for statistics\n- Sort by -count() for most common, -timestamp for newest\n", "inputSchema": { "type": "object", "properties": { @@ -7212,9 +7212,9 @@ "description": "The organization's slug. You can find a existing list of organizations you have access to using the `find_organizations()` tool." }, "dataset": { - "description": "Initial dataset hint: errors, logs, spans, metrics, profiles, or replays. Always pass it, including for natural language queries. The agent may correct it when configured.", "type": "string", - "enum": ["spans", "errors", "logs", "metrics", "profiles", "replays"] + "enum": ["spans", "errors", "logs", "metrics", "profiles", "replays"], + "description": "Dataset to search: errors, logs, spans, metrics, profiles, or replays. Pick the one that matches the question, including for natural language queries. The agent may correct it when configured." }, "query": { "description": "Natural language or Sentry event search query syntax.", @@ -7304,7 +7304,7 @@ "type": "boolean" } }, - "required": ["organizationSlug"] + "required": ["organizationSlug", "dataset"] }, "requiredScopes": ["event:read"], "skills": ["inspect", "triage", "seer"], diff --git a/packages/mcp-core/src/tools/catalog/search-events.test.ts b/packages/mcp-core/src/tools/catalog/search-events.test.ts index b3cfb3f40..bb3a8ebca 100644 --- a/packages/mcp-core/src/tools/catalog/search-events.test.ts +++ b/packages/mcp-core/src/tools/catalog/search-events.test.ts @@ -2,6 +2,7 @@ import { mswServer } from "@sentry/mcp-server-mocks"; import { APICallError, generateText } from "ai"; import { HttpResponse, http } from "msw"; import { beforeEach, describe, expect, it, vi } from "vitest"; +import { z } from "zod"; import { UserInputError } from "../../errors"; import searchEvents from "./search-events"; @@ -126,6 +127,24 @@ describe("search_events", () => { mockValidEventsValidation(); }); + it("requires a dataset so callers pick one up front", () => { + const schema = z.object(searchEvents.inputSchema); + + expect( + schema.safeParse({ + organizationSlug: "test-org", + query: "slowest api calls in the last 24 hours", + }).success, + ).toBe(false); + expect( + schema.safeParse({ + organizationSlug: "test-org", + dataset: "spans", + query: "slowest api calls in the last 24 hours", + }).success, + ).toBe(true); + }); + it("falls back to the original query when the AI provider is unavailable", async () => { mockGenerateText.mockRejectedValue( new APICallError({ @@ -1183,6 +1202,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "slow request duration metrics", @@ -1283,6 +1303,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "recent metrics", @@ -1370,6 +1391,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "recent profiles for /api/users", @@ -1441,6 +1463,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "recent profiles for /api/users", @@ -1511,6 +1534,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "recent profiles for /api/users", @@ -1566,6 +1590,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "count profiles by release", @@ -1639,6 +1664,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "recent profiles for /api/users", @@ -1713,6 +1739,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "production checkout replays with errors in the last day", @@ -1770,6 +1797,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "checkout replays yesterday", @@ -2016,6 +2044,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "recent errors with geo-only user data", @@ -2072,6 +2101,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "logs with user geo", @@ -2131,6 +2161,7 @@ describe("search_events", () => { const result = await searchEvents.handler( { organizationSlug: "test-org", + dataset: "errors", regionUrl: null, projectSlug: null, query: "spans with user geo", diff --git a/packages/mcp-core/src/tools/catalog/search-events.ts b/packages/mcp-core/src/tools/catalog/search-events.ts index 180c53d84..f97cdb36d 100644 --- a/packages/mcp-core/src/tools/catalog/search-events.ts +++ b/packages/mcp-core/src/tools/catalog/search-events.ts @@ -1,6 +1,5 @@ import { getActiveSpan, setTag } from "@sentry/core"; import { z } from "zod"; -import { setOrganizationContext } from "../../telem/organization"; import { UserInputError } from "../../errors"; import { hasAgentProvider } from "../../internal/agents/provider-factory"; import { withProviderFallback } from "../../internal/agents/provider-fallback"; @@ -13,6 +12,7 @@ import { ParamRegionUrl, } from "../../schema"; import { logWarn } from "../../telem/logging"; +import { setOrganizationContext } from "../../telem/organization"; import { scrubSensitiveText } from "../../telem/sentry"; import type { ServerContext } from "../../types"; import { @@ -395,7 +395,7 @@ export default defineTool({ "- metrics: Metric rows and aggregates: counters, gauges, distributions, values", "- profiles: Transaction/continuous profile results, profile IDs, profiled transactions", "- replays: Session replay results: rage clicks, dead clicks, visited pages, replay users", - "If the user says logs, log messages, error logs, or warning logs, choose logs instead of errors.", + "Pick logs for log messages (incl. error/warning logs); spans for API/HTTP calls, DB queries, latency.", "", "Replay searches return replay lists only; replay count()/avg()/sum() are not supported.", "", @@ -419,9 +419,8 @@ export default defineTool({ organizationSlug: ParamOrganizationSlug, dataset: z .enum(SEARCH_EVENTS_DATASETS) - .optional() .describe( - "Initial dataset hint: errors, logs, spans, metrics, profiles, or replays. Always pass it, including for natural language queries. The agent may correct it when configured.", + "Dataset to search: errors, logs, spans, metrics, profiles, or replays. Pick the one that matches the question, including for natural language queries. The agent may correct it when configured.", ), query: z .string() @@ -506,7 +505,7 @@ export default defineTool({ setOrganizationContext(organizationSlug); if (params.projectSlug) setTag("project.slug", params.projectSlug); - const inputDataset = params.dataset ?? "errors"; + const inputDataset = params.dataset; const hasStructuredQuery = looksLikeSentrySearchSyntax(params.query); const canApplyEnvironmentFilter = inputDataset !== "replays" && @@ -532,14 +531,11 @@ export default defineTool({ let timeSeries: { yAxis: string; interval: string | null } | null = null; const explicitSort = params.sort?.trim() || undefined; - const hasExplicitDataset = params.dataset !== undefined; const hasExplicitFields = hasFields(params.fields); const hasExplicitSort = explicitSort !== undefined; const hasExplicitPeriod = params.period !== undefined; - const hasExplicitTraceItemDataset = - hasExplicitDataset && isTraceItemDataset(inputDataset); const shouldTrustStructuredTraceSearch = - hasStructuredQuery && hasExplicitTraceItemDataset; + hasStructuredQuery && isTraceItemDataset(inputDataset); const environmentFilter = formatEnvironmentFilter(params.environment); const explicitStructuredTraceQuery = shouldTrustStructuredTraceSearch ? appendSearchFilter(params.query ?? "", environmentFilter) @@ -551,8 +547,8 @@ export default defineTool({ // (below) and to flag any requested environment that doesn't exist. Skipped // only when nothing references an environment — including a structured query // that skips the agent but puts `environment:` in the query string. - // Seer only translates into the dataset it is given, so it runs only when - // one is explicit. It only sees the natural language query, so skip it for + // Seer only translates into the dataset it is given, so it runs only for + // the datasets it has a strategy for. It only sees the natural language query, so skip it for // structured queries and explicit fields or sort, which the embedded agent // preserves. Like the UI, an explicit environment is added to Seer's query // afterwards.