Alert Investigate
Diagnose why a SigNoz alert fired: correlate the alert's own signal with
neighbor signals around the fire window and surface a ranked list of likely
causes with evidence. Companion to signoz-explaining-alerts (explain
decodes the rule statically; investigate diagnoses a specific incident).
Prerequisites
Needs SigNoz MCP tools (signoz_get_alert, signoz_get_alert_history,
signoz_execute_builder_query, signoz_query_metrics,
signoz_search_traces, signoz_search_logs, signoz_get_trace_details,
etc.). Confirm signoz_* tools are available before starting; if not, run
signoz-mcp-setup to initialize/repair the MCP connection. Without the
server there is no way to ground the analysis.
When to use
Use when the user wants to:
- Understand why a specific alert fired.
- Find the root cause of a recent incident triggered by an alert.
- Correlate the alert's signal with related metrics, traces, and logs.
- Distinguish "real signal" fires from flapping or threshold-mistuning.
Do NOT use when the user wants to:
- Understand what an alert monitors →
signoz-explaining-alerts.
- Create a new alert →
signoz-creating-alerts.
- Modify an alert (raise threshold, add hysteresis) → call
signoz_update_alert directly.
- Run a free-form investigation with no alert anchor →
signoz-generating-queries.
Required inputs
| Input |
Required |
Source if missing |
| Alert identifier (rule ID or name) |
yes |
$ARGUMENTS[0] or recent context |
| Time window |
no |
default to most recent fire from signoz_get_alert_history |
Fuzzy alert name (best-effort, read-only):
- Call
signoz_list_alert_rules, paginate, fuzzy-match the name.
- State the interpretation ("Investigating fire of 'High Error Rate -
Checkout' (id 42) at 14:32 UTC. If you meant a different alert or fire,
tell me.") and proceed.
If no firing transition exists in the queried lookback, stop — there is
nothing to investigate. Respond:
"Alert '[name]' has not fired in the last 7d, so there is no fire window
to investigate. Use signoz-explaining-alerts to walk through the rule,
or check whether the alert is enabled."
Workflow
Three tiers with strict early-stop gates. Tier 1 always runs; Tier 2 runs
only if Tier 1 confirms a real fire; Tier 3 runs only if Tier 2 surfaces
correlated anomalies. Skipping the gates fires hundreds of unnecessary
trace/log queries on quiet alerts.
Step 1: Resolve alert + fire window (Tier 0)
- Resolve the alert id via
signoz_list_alert_rules (paginated) if not
given.
- Call
signoz_get_alert for the full rule config (query, threshold,
resource scope the alert evaluated).
- Call
signoz_get_alert_history with timeRange: "7d", order: "desc",
and state omitted so the timeline includes firing and inactive
transitions. Paginate only when data.nextCursor exists (completeness
note reports hasMore: true): pass it as cursor, replace timeRange
with the note's resolved absolute start/end, and preserve the same
state/filter (including omission) and order. Stop when nextCursor is
absent / hasMore: false; never use offset or page fullness. If a
later intentional filter means "resolved"/"recovered", use inactive.
Enum: inactive|pending|recovering|firing|nodata|disabled; recovering
is a transient keep-firing state, not resolution. Pattern analysis needs
the complete transition set. Rows are emitted per label-group
fingerprint; do not interleave them. From the response:
- Build rule-wide incident windows from distinct rows where
overallStateChanged: true: overallState: "firing" opens an incident;
the next overallState: "inactive" closes it. Deduplicate matching
timestamps and sort by unixMilli ascending before pairing. Default to
the most recent incident unless $ARGUMENTS[1] selects another.
- Partition affected series by
fingerprint and retain each row's
labels. Use only stateChanged: true rows to decide when that group
fired/resolved and which group scopes Tier 1–3 queries.
- Note the fire pattern from rule-wide transitions or one named
fingerprint:
one-off → single fire with a long quiet period before/after.
sustained → fires that stayed firing for ≥ 1 evaluation cycle.
flapping → ≥ 3 fires within a 1h window, alternating fire/resolve.
recurring → fires at regular intervals (cron-like, e.g. hourly).
- Never infer flapping from different fingerprints. The pattern guides
tiers 2/3.
Step 2: Tier 1 — what fired and how hard (always runs)
Establishes the fire is real (vs. transient threshold tickle or flap) and
quantifies magnitude.
- Re-run the alert's primary query over
[fire_start - 30m, fire_start + 30m] via signoz_execute_builder_query for the alert's stored builder,
formula, PromQL, or ClickHouse envelope. Preserve positive bounds/order
so Tier 1 reproduces the stored alert. If a formula input is below 10000,
record truncation risk and compare at 10000 before ruling groups out. For
omissions, use 10000 on formula-input builder_query leaves and 100 on
standalone/formula results. Find leaves from every formula expression
(including disabled: true formulas), following references through the
dependency graph. This walk sets comparison bounds only; it does not prove
deterministic formula-to-formula order. Use v5 order: __result desc
for metrics/formulas or primary aggregation desc for logs/traces, never
dashboard orderBy. Time-series top-N ranks over the whole window and may
omit a short-lived local spike.
- Compute:
- Peak value during the fire window.
- Threshold breach magnitude:
(peak - threshold) / threshold * 100
for "above" alerts, inverted for "below".
- Fire duration: the rule's overall firing→inactive interval, or the
selected fingerprint's interval for a group-scoped investigation. Say
which.
- Pre-fire baseline: average in the 30m before fire start.
- Early-stop gate: if breach magnitude < 10% over threshold AND fire
duration < 1 evaluation window, classify as "marginal fire" (alert may be
too sensitive). Skip tiers 2/3 and go to Step 5 with a single hypothesis:
"threshold may be too tight, recommend tuning."
Step 3: Tier 2 — neighbor signals vs baseline (only if Tier 1 confirms a real breach)
Pull related signals for the alert's resource scope and compare fire window
to a baseline window.
- Pick a baseline window: same hour, previous day (
fire_start - 24h, fire_start - 24h + fire_duration). If the alert fired during a
known-anomalous time (deploy, weekly job), note it but still proceed.
- Look up neighbor signals for the alert's resource type — see
references/neighbor-signals.md. Common cases:
- Service (
service.name = X): error rate, p95/p99 latency, request
throughput, dependency error rates (if trace data available).
- Host / VM (
host.name = X): CPU, memory, disk I/O, network I/O.
- K8s pod / namespace: pod restarts, container CPU/memory limits, node
pressure, recent rollouts.
- For each neighbor signal: query both windows via
signoz_execute_builder_query or signoz_query_metrics, compute the
delta (% change fire vs baseline), rank by absolute delta.
- Early-stop gate: if no neighbor signal shows ≥ 25% deviation from
baseline, classify as "isolated fire: the alert's own signal moved but
nothing else did" (unusual, worth surfacing). Skip Tier 3 and go to Step 5
with hypotheses focused on the alert's own query (likely causes: data
source change, instrumentation change, downstream silent failure that only
shows in this metric).
Step 4: Tier 3 — traces and logs at the fire window (only if Tier 2 found correlated anomalies)
- Traces (if service-scoped and traces available): call
signoz_search_traces for the fire window with service.name = <scope>
AND has_error = true, cap at top 20. Group by name and
status_message; surface the sample's top 3 with one trace ID each (do
not treat a 20-row sample count as full-window frequency). Optionally call
signoz_get_trace_details for span attributes — pass the search row's
trace_id as traceId plus the same absolute fire-window start and
end, else the 6h default misses older incidents.
- Logs: call
signoz_search_logs with <scope_filter> AND
severity_text IN ('ERROR', 'FATAL'), cap at top 20 most recent. Group by
body pattern (or exception.type if present); surface the top 3
distinct messages with counts.
- Cross-reference: do the traces and logs point at the same downstream
service, dependency, or code path? If so, that becomes the leading
hypothesis.
See references/baseline-comparison.md for query templates that pair
fire-window and baseline-window calls cleanly.
Step 5: Structured output
Use this exact section order. Lead with the TL;DR (engineers under pressure
scan the top and stop once they have what they need). Every claim cites the
MCP query that produced it; no generic "check logs / verify connectivity"
filler.
1. TL;DR — one or two sentences max: leading hypothesis, overall
confidence, blast radius, single most useful next action. Example:
"checkoutservice error rate hit 12.4% (threshold 5%) for 8m at 14:32 UTC;
most likely cause is payments-api timing out (high confidence). Open trace
7af3a09b… to see the failing call."
If no hypothesis reaches medium confidence, lead with "No clear root cause
found." rather than a dressed-up low-confidence guess.
2. What fired — alert (id, name), fire window (absolute UTC + relative),
peak magnitude ("error rate hit 12.4% vs. 5% threshold, 148% over"), fire
duration, fire pattern (one-off/sustained/flapping/recurring/
marginal).
3. Investigation trail — scannable list of what was checked, ✅ for
confirmed and ❌ for ruled out, each with a one-line finding, e.g.:
- ✅ Tier 1: peak error rate 12.4%, fire was real (not marginal).
- ✅ Tier 2: payments error rate +8900%, p99 +1180%; downstream cascade.
- ❌ CPU / memory pressure: flat through the fire window.
- ✅ Tier 3: 30 error traces all hit payments-api, same message.
4. Likely causes (ranked, max 3) — each has:
- Hypothesis: one specific sentence. Bad: "service is unhealthy". Good:
"checkout is timing out on calls to payments-api".
- Evidence: supporting numbers from tiers 1/2/3 with the underlying query
inline (neighbor signal, delta vs baseline, trace/log pattern) so the user
can re-run it.
- Confidence:
high requires ≥2 of {temporal precedence, topology /
dependency edge, shared service or entity, correlated metric/log/trace
evidence, recent deploy or config change}. medium is one tier's evidence
with ≥1 of those. low is a single signal with no corroboration — label it
a "co-occurring signal," not a cause.
If only Tier 1 ran (marginal / no neighbor anomalies), output fewer
hypotheses at low confidence and call out the limitation.
5. Ruled out — short but explicit: candidates the evidence eliminated and
the one-line reason. Skip only if there is nothing meaningful; if you
considered and dropped something, say so here.
6. Suggested next steps — concrete, SigNoz-native handles the user can
act on immediately:
- Specific trace/dashboard/alert to open ("open trace
7af3a09b… in the
SigNoz UI").
- Specific query to run with
signoz-generating-queries: paste the exact
filter and time window.
- "Tune this alert" if marginal: name the field (
matchType, target,
recoveryTarget) and the change to make via signoz_update_alert.
- "Open an incident" / "page the owning team" if the cause is cross-service.
No generic filler ("verify connectivity", "check dashboards").
Mirror up to 3 of these next steps as host follow-up intents: the most
actionable, alert-scoped ones. Keep the rest in the report prose (the chip
surface is capped; the prose is not).
Out of scope (v1)
- Deploy / config-change correlation: SigNoz MCP exposes no deployments
tool; do not fabricate one. Surface a user-mentioned deploy as context, but
don't claim it caused the fire without signal evidence.
- Cross-service blast-radius walking (downstream callers): out of scope
to keep context bounded.
- Long-horizon historical baselines: Tier 2 compares to one prior-day
window, not weekly/monthly seasonality. If asked "is this normal for a
Friday afternoon", suggest an anomaly alert (
signoz-creating-alerts with
anomaly_rule).
Guardrails
- Early-stop gates are mandatory, not optional optimizations — skipping
them explodes context.
- Anchor every claim to a real MCP query result; no speculation, no
fabricated identifiers (trace IDs, span names, rule IDs, channel/deploy IDs
must all come from real responses). If evidence is missing, lower
confidence and say so.
- Correlation ≠ causation: only call something a cause with ≥2 converging
signals (see Confidence in Step 5); otherwise it's a "co-occurring signal."
- Report uncertainty honestly: if nothing reaches medium confidence, the
answer is "No clear root cause found" — false positives waste incident time
more than false negatives.
- Keep it a triage card, not a postmortem — every section must add
evidence the reader can't already see in the alert payload; drop generic
advice.
- Prefer resource-attribute filters in every drill-down query (SigNoz MCP
guideline; directly affects query speed at scale).
- Read-only: never call
signoz_update_alert; surface tuning as a
next-step recommendation.
- State fire windows in UTC + relative ("14:32 UTC, 2h ago") so
autonomous and interactive consumers agree on the window.
Example (full 3-tier run)
User: "Why did the checkout error rate alert fire?"
Agent:
Resolves alert: "High Error Rate - Checkout" (id 42).
signoz_get_alert_history → most recent fire 2h ago at 14:32 UTC,
sustained 8m, single fire (not flapping).
Tier 1: re-runs error-rate formula over [14:02, 15:02]. Peak 12.4%
(vs 5% threshold, 148% over), pre-fire baseline 0.3%. Real fire.
Tier 2 (service.name = checkout): p99 4.1s vs 320ms (+1180%);
throughput −42%; downstream payments error rate 18% vs 0.2% (+8900%);
CPU/memory flat.
Tier 3: error traces top op POST /checkout/submit, message "context
deadline exceeded calling payments-api", 30 traces same downstream URL;
matching "payments client timeout" logs, 142 occurrences.
Output:
TL;DR: checkoutservice error rate hit 12.4% (threshold 5%) for 8m at
14:32 UTC. Most likely cause: payments-api timing out (high confidence;
converging trace + log + neighbor evidence). Open trace 7af3a09b….
- What fired: alert 42 fired 2h ago at 14:32 UTC, sustained 8m; error
rate peaked 12.4% (148% over threshold).
- Investigation trail:
- ✅ Tier 1: peak 12.4% vs 5%, pre-fire baseline 0.3%. Real fire.
- ✅ Tier 2: payments error rate +8900%, p99 +1180%, throughput −42%.
- ❌ CPU / memory on checkout: flat.
- ✅ Tier 3: 30 error traces all hit payments-api with
context deadline exceeded; 142 matching timeout logs.
- Likely causes (high): payments errors cascading into checkout.
Evidence converges across topology (checkout → payments edge), temporal
precedence (payments errors lead checkout p99), shared entity (every
error trace targets the payments-api URL).
- Ruled out: checkout-side resource pressure (CPU/memory flat);
upstream traffic spike (throughput dropped, didn't spike).
- Next steps: open trace
7af3a09b…; if payments has its own alert,
run signoz-explaining-alerts on it, else signoz-generating-queries
for payments error rate over the last 4h.
Same structure applies to other patterns, differing only in where the tiers
stop. A flapping / marginal fire (e.g. CPU hovering 5% over an 80%
threshold, matchType = at_least_once) early-stops after Tier 1 → output is
a low-confidence "threshold tuning, not an incident" with a matchType →
on_average / recoveryTarget fix. A log-volume fire (no service
filter, groupBy service.name) resolves the firing series from history, then
runs all three tiers on that service (e.g. Tier 2 shows collapsed CPU/memory
- pod restarts, Tier 3 shows "OOMKilled restarting" logs → high-confidence
OOM cause, check container memory limits).
Additional resources
references/neighbor-signals.md: resource type (service / host / k8s) →
neighbor signals to pull in Tier 2.
references/baseline-comparison.md: query templates pairing fire-window and
baseline-window calls, including signoz_execute_builder_query format.
signoz-explaining-alerts: decode the rule before investigating.
signoz-generating-queries: ad-hoc follow-up queries on the same scope.
1---2name: signoz-investigating-alerts3description: Diagnose why a SigNoz alert fired by correlating the alert's own signal with neighbor signals (error rate, latency, throughput, CPU/memory), traces, and logs around the fire window, and rank likely causes. Use whenever the user asks "why did this alert fire", "what caused alert X", "investigate this alert", "RCA for the alert that paged me", "what's wrong with [service]" in the context of a recent fire, or otherwise asks for a root-cause analysis of a firing or recently-fired alert. Read-only; does not modify any alert or notification.4---56# Alert Investigate78Diagnose why a SigNoz alert fired: correlate the alert's own signal with9neighbor signals around the fire window and surface a ranked list of likely10causes with evidence. Companion to `signoz-explaining-alerts` (explain11decodes the rule statically; investigate diagnoses a specific incident).1213## Prerequisites1415Needs SigNoz MCP tools (`signoz_get_alert`, `signoz_get_alert_history`,16`signoz_execute_builder_query`, `signoz_query_metrics`,17`signoz_search_traces`, `signoz_search_logs`, `signoz_get_trace_details`,18etc.). Confirm `signoz_*` tools are available before starting; if not, run19`signoz-mcp-setup` to initialize/repair the MCP connection. Without the20server there is no way to ground the analysis.2122## When to use2324Use when the user wants to:25- Understand why a specific alert fired.26- Find the root cause of a recent incident triggered by an alert.27- Correlate the alert's signal with related metrics, traces, and logs.28- Distinguish "real signal" fires from flapping or threshold-mistuning.2930Do NOT use when the user wants to:31- Understand what an alert monitors → `signoz-explaining-alerts`.32- Create a new alert → `signoz-creating-alerts`.33- Modify an alert (raise threshold, add hysteresis) → call34 `signoz_update_alert` directly.35- Run a free-form investigation with no alert anchor →36 `signoz-generating-queries`.3738## Required inputs3940| Input | Required | Source if missing |41|---|---|---|42| Alert identifier (rule ID or name) | yes | `$ARGUMENTS[0]` or recent context |43| Time window | no | default to most recent fire from `signoz_get_alert_history` |4445Fuzzy alert name (best-effort, read-only):461. Call `signoz_list_alert_rules`, paginate, fuzzy-match the name.472. State the interpretation ("Investigating fire of 'High Error Rate -48 Checkout' (id 42) at 14:32 UTC. If you meant a different alert or fire,49 tell me.") and proceed.5051If no firing transition exists in the queried lookback, **stop** — there is52nothing to investigate. Respond:53> "Alert '[name]' has not fired in the last 7d, so there is no fire window54> to investigate. Use `signoz-explaining-alerts` to walk through the rule,55> or check whether the alert is enabled."5657## Workflow5859Three tiers with strict early-stop gates. Tier 1 always runs; Tier 2 runs60only if Tier 1 confirms a real fire; Tier 3 runs only if Tier 2 surfaces61correlated anomalies. Skipping the gates fires hundreds of unnecessary62trace/log queries on quiet alerts.6364### Step 1: Resolve alert + fire window (Tier 0)65661. Resolve the alert id via `signoz_list_alert_rules` (paginated) if not67 given.682. Call `signoz_get_alert` for the full rule config (query, threshold,69 resource scope the alert evaluated).703. Call `signoz_get_alert_history` with `timeRange: "7d"`, `order: "desc"`,71 and `state` omitted so the timeline includes firing and inactive72 transitions. Paginate only when `data.nextCursor` exists (completeness73 note reports `hasMore: true`): pass it as `cursor`, replace `timeRange`74 with the note's resolved absolute `start`/`end`, and preserve the same75 state/filter (including omission) and order. Stop when `nextCursor` is76 absent / `hasMore: false`; never use `offset` or page fullness. If a77 later intentional filter means "resolved"/"recovered", use `inactive`.78 Enum: `inactive|pending|recovering|firing|nodata|disabled`; `recovering`79 is a transient keep-firing state, not resolution. Pattern analysis needs80 the complete transition set. Rows are emitted per label-group81 `fingerprint`; do not interleave them. From the response:82 - **Build rule-wide incident windows** from distinct rows where83 `overallStateChanged: true`: `overallState: "firing"` opens an incident;84 the next `overallState: "inactive"` closes it. Deduplicate matching85 timestamps and sort by `unixMilli` ascending before pairing. Default to86 the most recent incident unless `$ARGUMENTS[1]` selects another.87 - **Partition affected series by `fingerprint`** and retain each row's88 labels. Use only `stateChanged: true` rows to decide when that group89 fired/resolved and which group scopes Tier 1–3 queries.90 - **Note the fire pattern** from rule-wide transitions or one named91 fingerprint:92 - `one-off` → single fire with a long quiet period before/after.93 - `sustained` → fires that stayed firing for ≥ 1 evaluation cycle.94 - `flapping` → ≥ 3 fires within a 1h window, alternating fire/resolve.95 - `recurring` → fires at regular intervals (cron-like, e.g. hourly).96 - Never infer flapping from different fingerprints. The pattern guides97 tiers 2/3.9899### Step 2: Tier 1 — what fired and how hard (always runs)100101Establishes the fire is real (vs. transient threshold tickle or flap) and102quantifies magnitude.1031041. Re-run the alert's primary query over `[fire_start - 30m, fire_start +105 30m]` via `signoz_execute_builder_query` for the alert's stored builder,106 formula, PromQL, or ClickHouse envelope. Preserve positive bounds/order107 so Tier 1 reproduces the stored alert. If a formula input is below 10000,108 record truncation risk and compare at 10000 before ruling groups out. For109 omissions, use 10000 on formula-input `builder_query` leaves and 100 on110 standalone/formula results. Find leaves from every formula expression111 (including `disabled: true` formulas), following references through the112 dependency graph. This walk sets comparison bounds only; it does not prove113 deterministic formula-to-formula order. Use v5 `order`: `__result desc`114 for metrics/formulas or primary aggregation desc for logs/traces, never115 dashboard `orderBy`. Time-series top-N ranks over the whole window and may116 omit a short-lived local spike.1172. Compute:118 - **Peak value** during the fire window.119 - **Threshold breach magnitude**: `(peak - threshold) / threshold * 100`120 for "above" alerts, inverted for "below".121 - **Fire duration**: the rule's overall firing→inactive interval, or the122 selected fingerprint's interval for a group-scoped investigation. Say123 which.124 - **Pre-fire baseline**: average in the 30m before fire start.1253. **Early-stop gate**: if breach magnitude < 10% over threshold AND fire126 duration < 1 evaluation window, classify as "marginal fire" (alert may be127 too sensitive). Skip tiers 2/3 and go to Step 5 with a single hypothesis:128 "threshold may be too tight, recommend tuning."129130### Step 3: Tier 2 — neighbor signals vs baseline (only if Tier 1 confirms a real breach)131132Pull related signals for the alert's resource scope and compare fire window133to a baseline window.1341351. **Pick a baseline window**: same hour, previous day (`fire_start - 24h,136 fire_start - 24h + fire_duration`). If the alert fired during a137 known-anomalous time (deploy, weekly job), note it but still proceed.1382. **Look up neighbor signals** for the alert's resource type — see139 `references/neighbor-signals.md`. Common cases:140 - **Service** (`service.name = X`): error rate, p95/p99 latency, request141 throughput, dependency error rates (if trace data available).142 - **Host / VM** (`host.name = X`): CPU, memory, disk I/O, network I/O.143 - **K8s pod / namespace**: pod restarts, container CPU/memory limits, node144 pressure, recent rollouts.1453. For each neighbor signal: query both windows via146 `signoz_execute_builder_query` or `signoz_query_metrics`, compute the147 delta (% change fire vs baseline), rank by absolute delta.1484. **Early-stop gate**: if no neighbor signal shows ≥ 25% deviation from149 baseline, classify as "isolated fire: the alert's own signal moved but150 nothing else did" (unusual, worth surfacing). Skip Tier 3 and go to Step 5151 with hypotheses focused on the alert's own query (likely causes: data152 source change, instrumentation change, downstream silent failure that only153 shows in this metric).154155### Step 4: Tier 3 — traces and logs at the fire window (only if Tier 2 found correlated anomalies)1561571. **Traces** (if service-scoped and traces available): call158 `signoz_search_traces` for the fire window with `service.name = <scope>`159 AND `has_error = true`, cap at top 20. Group by `name` and160 `status_message`; surface the sample's top 3 with one trace ID each (do161 not treat a 20-row sample count as full-window frequency). Optionally call162 `signoz_get_trace_details` for span attributes — pass the search row's163 `trace_id` as `traceId` **plus the same absolute fire-window `start` and164 `end`**, else the 6h default misses older incidents.1652. **Logs**: call `signoz_search_logs` with `<scope_filter>` AND166 `severity_text IN ('ERROR', 'FATAL')`, cap at top 20 most recent. Group by167 `body` pattern (or `exception.type` if present); surface the top 3168 distinct messages with counts.1693. **Cross-reference**: do the traces and logs point at the same downstream170 service, dependency, or code path? If so, that becomes the leading171 hypothesis.172173See `references/baseline-comparison.md` for query templates that pair174fire-window and baseline-window calls cleanly.175176### Step 5: Structured output177178Use this exact section order. Lead with the TL;DR (engineers under pressure179scan the top and stop once they have what they need). Every claim cites the180MCP query that produced it; no generic "check logs / verify connectivity"181filler.182183**1. TL;DR** — one or two sentences max: leading hypothesis, overall184confidence, blast radius, single most useful next action. Example:185> "checkoutservice error rate hit 12.4% (threshold 5%) for 8m at 14:32 UTC;186> most likely cause is payments-api timing out (high confidence). Open trace187> `7af3a09b…` to see the failing call."188189If no hypothesis reaches medium confidence, lead with "No clear root cause190found." rather than a dressed-up low-confidence guess.191192**2. What fired** — alert (id, name), fire window (absolute UTC + relative),193peak magnitude ("error rate hit 12.4% vs. 5% threshold, 148% over"), fire194duration, fire pattern (`one-off`/`sustained`/`flapping`/`recurring`/195`marginal`).196197**3. Investigation trail** — scannable list of what was checked, ✅ for198confirmed and ❌ for ruled out, each with a one-line finding, e.g.:199- ✅ Tier 1: peak error rate 12.4%, fire was real (not marginal).200- ✅ Tier 2: payments error rate +8900%, p99 +1180%; downstream cascade.201- ❌ CPU / memory pressure: flat through the fire window.202- ✅ Tier 3: 30 error traces all hit payments-api, same message.203204**4. Likely causes** (ranked, max 3) — each has:205- **Hypothesis**: one specific sentence. Bad: "service is unhealthy". Good:206 "checkout is timing out on calls to payments-api".207- **Evidence**: supporting numbers from tiers 1/2/3 with the underlying query208 inline (neighbor signal, delta vs baseline, trace/log pattern) so the user209 can re-run it.210- **Confidence**: `high` requires ≥2 of {temporal precedence, topology /211 dependency edge, shared service or entity, correlated metric/log/trace212 evidence, recent deploy or config change}. `medium` is one tier's evidence213 with ≥1 of those. `low` is a single signal with no corroboration — label it214 a "co-occurring signal," not a cause.215216If only Tier 1 ran (marginal / no neighbor anomalies), output fewer217hypotheses at `low` confidence and call out the limitation.218219**5. Ruled out** — short but explicit: candidates the evidence eliminated and220the one-line reason. Skip only if there is nothing meaningful; if you221considered and dropped something, say so here.222223**6. Suggested next steps** — concrete, SigNoz-native handles the user can224act on immediately:225- Specific trace/dashboard/alert to open ("open trace `7af3a09b…` in the226 SigNoz UI").227- Specific query to run with `signoz-generating-queries`: paste the exact228 filter and time window.229- "Tune this alert" if marginal: name the field (`matchType`, `target`,230 `recoveryTarget`) and the change to make via `signoz_update_alert`.231- "Open an incident" / "page the owning team" if the cause is cross-service.232233No generic filler ("verify connectivity", "check dashboards").234235**Mirror up to 3** of these next steps as host follow-up intents: the most236actionable, alert-scoped ones. Keep the rest in the report prose (the chip237surface is capped; the prose is not).238239## Out of scope (v1)240241- **Deploy / config-change correlation**: SigNoz MCP exposes no deployments242 tool; do not fabricate one. Surface a user-mentioned deploy as context, but243 don't claim it caused the fire without signal evidence.244- **Cross-service blast-radius walking** (downstream callers): out of scope245 to keep context bounded.246- **Long-horizon historical baselines**: Tier 2 compares to one prior-day247 window, not weekly/monthly seasonality. If asked "is this normal for a248 Friday afternoon", suggest an anomaly alert (`signoz-creating-alerts` with249 `anomaly_rule`).250251## Guardrails252253- **Early-stop gates are mandatory**, not optional optimizations — skipping254 them explodes context.255- **Anchor every claim to a real MCP query result**; no speculation, no256 fabricated identifiers (trace IDs, span names, rule IDs, channel/deploy IDs257 must all come from real responses). If evidence is missing, lower258 confidence and say so.259- **Correlation ≠ causation**: only call something a cause with ≥2 converging260 signals (see Confidence in Step 5); otherwise it's a "co-occurring signal."261- **Report uncertainty honestly**: if nothing reaches medium confidence, the262 answer is "No clear root cause found" — false positives waste incident time263 more than false negatives.264- **Keep it a triage card, not a postmortem** — every section must add265 evidence the reader can't already see in the alert payload; drop generic266 advice.267- **Prefer resource-attribute filters** in every drill-down query (SigNoz MCP268 guideline; directly affects query speed at scale).269- **Read-only**: never call `signoz_update_alert`; surface tuning as a270 next-step recommendation.271- **State fire windows in UTC + relative** ("14:32 UTC, 2h ago") so272 autonomous and interactive consumers agree on the window.273274## Example (full 3-tier run)275276**User:** "Why did the checkout error rate alert fire?"277278**Agent:**2791. Resolves alert: "High Error Rate - Checkout" (id 42).2802. `signoz_get_alert_history` → most recent fire 2h ago at 14:32 UTC,281 sustained 8m, single fire (not flapping).2823. **Tier 1**: re-runs error-rate formula over `[14:02, 15:02]`. Peak 12.4%283 (vs 5% threshold, 148% over), pre-fire baseline 0.3%. Real fire.2844. **Tier 2** (`service.name = checkout`): p99 4.1s vs 320ms (+1180%);285 throughput −42%; downstream `payments` error rate 18% vs 0.2% (+8900%);286 CPU/memory flat.2875. **Tier 3**: error traces top op `POST /checkout/submit`, message "context288 deadline exceeded calling payments-api", 30 traces same downstream URL;289 matching "payments client timeout" logs, 142 occurrences.2906. **Output**:291292 > **TL;DR**: checkoutservice error rate hit 12.4% (threshold 5%) for 8m at293 > 14:32 UTC. Most likely cause: payments-api timing out (high confidence;294 > converging trace + log + neighbor evidence). Open trace `7af3a09b…`.295296 - **What fired**: alert 42 fired 2h ago at 14:32 UTC, sustained 8m; error297 rate peaked 12.4% (148% over threshold).298 - **Investigation trail**:299 - ✅ Tier 1: peak 12.4% vs 5%, pre-fire baseline 0.3%. Real fire.300 - ✅ Tier 2: payments error rate +8900%, p99 +1180%, throughput −42%.301 - ❌ CPU / memory on checkout: flat.302 - ✅ Tier 3: 30 error traces all hit payments-api with `context deadline303 exceeded`; 142 matching timeout logs.304 - **Likely causes** (high): payments errors cascading into checkout.305 Evidence converges across topology (checkout → payments edge), temporal306 precedence (payments errors lead checkout p99), shared entity (every307 error trace targets the payments-api URL).308 - **Ruled out**: checkout-side resource pressure (CPU/memory flat);309 upstream traffic spike (throughput dropped, didn't spike).310 - **Next steps**: open trace `7af3a09b…`; if payments has its own alert,311 run `signoz-explaining-alerts` on it, else `signoz-generating-queries`312 for payments error rate over the last 4h.313314Same structure applies to other patterns, differing only in where the tiers315stop. A **flapping / marginal** fire (e.g. CPU hovering 5% over an 80%316threshold, `matchType = at_least_once`) early-stops after Tier 1 → output is317a low-confidence "threshold tuning, not an incident" with a `matchType` →318`on_average` / `recoveryTarget` fix. A **log-volume** fire (no service319filter, `groupBy service.name`) resolves the firing series from history, then320runs all three tiers on that service (e.g. Tier 2 shows collapsed CPU/memory321+ pod restarts, Tier 3 shows "OOMKilled restarting" logs → high-confidence322OOM cause, check container memory limits).323324## Additional resources325326- `references/neighbor-signals.md`: resource type (service / host / k8s) →327 neighbor signals to pull in Tier 2.328- `references/baseline-comparison.md`: query templates pairing fire-window and329 baseline-window calls, including `signoz_execute_builder_query` format.330- `signoz-explaining-alerts`: decode the rule before investigating.331- `signoz-generating-queries`: ad-hoc follow-up queries on the same scope.