Skip to content

Series

Build a time series over the offloaded cohort in the customer-owned S3 overflow bucket, over any window, with optional group-by on a label. The cohort is the events the Receiver diverted out of the SIEM; the Retriever reads them byte-range-scoped from the bucket in place. Returns counts per bucket; mode auto-selects between exact counts (small queries) and sampled estimates (high-volume or long-window). When sampled, series shape and top-value ranking stay reliable; individual bucket counts become approximate.

Example

You

hourly series of offloaded Payment_Retry, last 30d, by tenant

Log10x

720 hourly buckets over the offloaded cohort, broken down by tenant. The window holds ~8B events, too many to count exactly, so per-bucket numbers are sampled estimates. Series shape and the tenant ranking are reliable.

Top tenants: acme-corp 41% · globex 28% · initech 18% · umbrella 13%.

More to ask

  • "force exact counts on a 6h window of Auth_Failed"
  • "hourly Retry_Exhausted last 7d, by service"
  • "sampled at 200 events per sub-window, last week"

Prerequisites

This tool requires the Retriever deployed. The Reporter is optional; without it, mode selection falls back to a window-length heuristic.

Schema and samples

Input example

Real call against the demo env (captured by scripts/capture-tool-envelopes.mjs).

{
  "pattern": "open_telemetry_opensearchexporter_clientLogger_LogRoundTrip_open_telemetry_opensearchexporter_v_go_github_opensearch_project",
  "from": "now-1h",
  "to": "now",
  "bucket_size": "5m",
  "view": "summary"
}
Input schema

Agent-facing JSON Schema (the canonical shape the MCP server publishes via tools/list):

{
  "type": "object",
  "properties": {
    "pattern": {
      "type": "string",
      "description": "Reporter-named pattern (Symbol Message). Auto-translated to `tenx_user_pattern == \"<name>\"` Bloom-filter expression. Use this when the agent has a pattern name from event_lookup / top_patterns / cost_drivers. Mutually exclusive with `search`; `search` wins if both provided."
    },
    "search": {
      "type": "string",
      "description": "Bloom-filter search expression using the TenX subset. Tightly bound queries (e.g., `tenx_user_pattern == \"PaymentRetry\"`) get the cheapest fetch path. Pattern-bound expressions are also what unlocks the Reporter-driven cost heuristic — without one, mode selection falls back to window-length only. Pass `pattern` instead for the common case of scoping to one Reporter-named pattern."
    },
    "from": {
      "type": "string",
      "description": "Start of the query window. ISO8601, epoch millis, or relative (`now-1h`, `now-7d`, `now-30d`)."
    },
    "to": {
      "type": "string",
      "default": "now",
      "description": "End of the query window. Same grammar as `from`. Default `now`."
    },
    "filters": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "In-memory JS filters applied after the Bloom-scoped fetch (AND-combined)."
    },
    "target": {
      "type": "string",
      "description": "Target app prefix. Defaults to __SAVE_LOG10X_RETRIEVER_TARGET__."
    },
    "bucket_size": {
      "type": "string",
      "default": "5m",
      "description": "Time bucket granularity (`1m`, `5m`, `1h`, `1d`). Determines the resolution of the output series."
    },
    "group_by": {
      "type": "string",
      "description": "Optional enrichment field to group the series by — e.g., `tenx_user_service`, `severity_level`, `k8s_namespace`. Top-1000 group values are retained; tail collapsed to `_other_`."
    },
    "fidelity": {
      "type": "string",
      "default": "auto",
      "description": "`auto` (tool decides via Reporter volume + window length), `full` (force exact aggregation — may exceed Lambda budget), `per_window_sampled` (force sampling, default K=1000 per sub-window), or `per_window_sampled:K` (custom K)."
    },
    "environment": {
      "type": "string",
      "description": "Environment nickname — required if multi-env."
    },
    "view": {
      "type": "string",
      "const": "summary",
      "default": "summary",
      "description": "summary returns the typed envelope (data.mode, data.bucket_seconds, data.series_count, data.points_returned, data.top_groups, data.caveats, data.human_summary). The deprecated markdown view was removed; data.human_summary carries the prose distillation for chat rendering."
    }
  },
  "required": [
    "from"
  ],
  "additionalProperties": false
}

Source: src/tools/retriever-series.ts.

Output example

Real envelope from the demo env. view: "summary" returns the full StructuredOutput with typed data. Long arrays + base64 PNG bodies trimmed for readability; the real call returns them in full.

Headline (the 1-line agent-facing answer):

log10x_retriever_series ran but its retriever precondition is not configured.

When the Retriever is not wired up, the tool returns a typed not_configured envelope. The remediation points at the Retriever (rq-1), standing up the S3 overflow bucket plus index, not the metrics backend, since this tool reads the offloaded cohort rather than a TSDB.

{
  "schema_version": "1.0",
  "schema_epoch": "2026-05-25",
  "tool": "log10x_retriever_series",
  "generated_at": "2026-05-26T15:38:35.780Z",
  "view": "summary",
  "summary": {
    "headline": "`log10x_retriever_series` ran but its retriever precondition is not configured. This is an expected state, not a failure: read data.payload.remediation, surface the fix, and continue the chain without this tool."
  },
  "data": {
    "status": "not_configured",
    "precondition": "retriever",
    "remediation": "## Retriever not configured\n\n..."
  },
  "actions": [
    {
      "tool": "log10x_advise_retriever",
      "args": {},
      "reason": "stand up the Retriever S3 archive + index, then re-run",
      "role": "required-next"
    }
  ],
  "truncated": false,
  "warnings": []
}
Output schema

The data block inside the StructuredOutput envelope. On a successful run:

interface ToolData {
  status: 'success' | 'no_signal' | 'refused';
  ok: boolean;
  mode: 'full' | 'per_window_sampled';
  from: string;
  to: string;
  window_ms: number;
  group_by?: string;
  actual_events: number;
  points_returned: number;
  series_count: number;
  top_groups: Array<{ group: string; count: number }>;
  wall_time_ms: number;
  human_summary: string;
}

When the Retriever is not configured, data instead carries { status: 'not_configured', precondition: 'retriever', remediation } (see the output example above).

Envelope-level fields the agent should also read: summary.headline (1-line answer), actions[] (next-call chain hints as {tool, args, reason}), truncated: boolean, images[] (PNG attachments where applicable), schema_epoch (engine-ID stability boundary).