Alert Investigate
Diagnose why a SigNoz alert fired. The skill correlates the alert's own
signal with neighbor signals around the fire window, and surfaces a
ranked list of likely causes with supporting evidence. It is the
companion to signoz-explaining-alerts: explain decodes the rule
statically; investigate diagnoses a specific incident.
Prerequisites
This skill calls SigNoz MCP server tools heavily (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.). Before running the workflow,
confirm the signoz_* tools are available. If they are not, the
SigNoz MCP server is not installed or configured; run signoz-mcp-setup first
to initialize or repair the MCP connection. The investigation depends on
correlating multiple MCP queries; without the server there is no way to ground
the analysis.
When to use
Use this skill 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 is configured to monitor →
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 ad-hoc investigation without an alert as the 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 |
If the alert name is fuzzy, this skill is 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."
- Proceed.
If no firing transition exists in the queried lookback window, stop: there is
nothing to investigate. Respond with:
"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
The investigation runs in 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 produces 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, needed to know
what query, threshold, and resource scope the alert evaluated.
- First call
signoz_get_alert_history with timeRange: "7d" and
order: "desc"; omit state so the timeline includes firing and inactive
transitions. Continue only when data.nextCursor exists (the completeness
note also reports hasMore: true). Pass it as cursor, replace timeRange
with the note's resolved absolute start and end, and preserve the same
state/filter (including omission) and order. Stop when nextCursor is absent
/ the note reports hasMore: false; never use offset or page fullness.
If a later intentional state filter means "resolved" / "recovered", use
inactive. The enum is 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: an overallState: "firing" transition 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
and resolved, and which group should scope 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., every hour).
- Never infer flapping from different fingerprints. The pattern guides tiers 2/3.
Step 2: Tier 1 (what fired and how hard)
This tier always runs. It establishes the fire is real (vs. transient
threshold tickle or flap) and quantifies the magnitude.
- Re-run the alert's primary query over a window centered on the fire
start:
[fire_start - 30m, fire_start + 30m].
- Use
signoz_execute_builder_query for the alert's stored builder,
formula, PromQL, or ClickHouse query 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 value in the 30m before fire start.
- Early-stop gate: if the breach magnitude is < 10% over the
threshold AND the fire duration is < 1 evaluation window, classify
as "marginal fire"; the alert may be too sensitive. Skip tiers 2
and 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)
Run only if Tier 1 confirms a real breach. Pull related signals for the
same resource scope as the alert and compare the fire window to a
baseline window.
Pick a baseline window. Use the 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 in the output but still proceed.
Look up neighbor signals for the alert's resource type. See
references/neighbor-signals.md for the lookup table. Common cases:
- Service-level alert (
service.name = X): pull error rate,
p95/p99 latency, request throughput, dependency error rates if
trace data is available.
- Host / VM alert (
host.name = X): CPU, memory, disk I/O,
network I/O.
- K8s pod / namespace alert: pod restarts, container CPU/memory
limits, node pressure, recent rollouts.
For each neighbor signal:
- Query both windows (fire + baseline) via
signoz_execute_builder_query or signoz_query_metrics.
- Compute the delta (% change in fire window 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." This is unusual and 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)
Run only if Tier 2 found correlated neighbor anomalies. Drill into
specific failing operations.
Traces (if the alert is service-scoped and traces are
available):
- Call
signoz_search_traces for the fire window with filter:
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 call a 20-row sample count 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; otherwise the 6h default misses older incidents.
Logs for the fire window:
- Call
signoz_search_logs with filter:
<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: Build the structured output
Use this exact section order. Lead with a TL;DR, because engineers
under pressure scan the top first and stop reading once they have what
they need. Every claim cites the MCP query that produced it, with no
generic "check logs / verify connectivity" filler.
1. TL;DR: one or two sentences, no more. Leading hypothesis,
overall confidence, blast radius, and the 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, the leading line is
"No clear root cause found." rather than a low-confidence guess
dressed up as the answer.
2. What fired
The alert (id, name), the fire window (absolute UTC + relative),
peak magnitude ("error rate hit 12.4% vs. 5% threshold, 148% over"),
fire duration, and the fire pattern (one-off / sustained /
flapping / recurring / marginal).
3. Investigation trail
A scannable list of what was checked, with ✅ for confirmed signals
and ❌ for ruled out, each followed by a one-line finding. The point
is that the reader can see what work the AI did and what it found.
Example:
- ✅ 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 cause has three parts:
- Hypothesis: one sentence, specific. Bad: "service is unhealthy".
Good: "checkout is timing out on calls to payments-api".
- Evidence: the supporting numbers from tiers 1/2/3, with the
underlying query inline so the user can re-run it. State the
neighbor signal, the delta vs baseline, the trace/log pattern that
supports 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 at least one of those
signals. low is a single signal moved with no corroboration;
in that case label it a "co-occurring signal," not a cause.
If only Tier 1 ran (marginal fire / no neighbor anomalies), output
fewer hypotheses with low confidence and explicitly call out the
limitation.
5. Ruled out
Short but explicit. List candidates the evidence eliminated and the
one-line reason why. Skip the section if there's nothing meaningful
to rule out, but if you considered something and dropped it, say so
here so the user doesn't waste time re-checking it.
6. Suggested next steps
Action items the user can take. Be concrete and use SigNoz-native
handles so the user can act immediately:
- Specific trace, dashboard, or alert to open
(e.g., "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 the fire was marginal: name the field
(
matchType, target, recoveryTarget) and the change to make
via signoz_update_alert.
- "Open an incident" or "page the owning team" if the cause is
cross-service.
Do not pad with generic advice ("verify connectivity", "check
dashboards"); that's noise during an active incident.
Mirroring as navigation chips. Mirror up to 3 of these "Suggested
next steps" as host follow-up intents: the most actionable,
alert-scoped ones. Keep the rest in the report prose so the user has
the full picture. The chip surface is capped; the prose is not.
Out of scope (v1)
- Deployment / config-change correlation: SigNoz MCP does not
expose a deployments tool; do not fabricate one. If the user
mentions a recent deploy, surface it as context but don't claim it
caused the fire without the signal evidence.
- Cross-service blast-radius walking: investigating downstream
callers of the alert's service. Out of scope to keep context
bounded.
- Long-horizon historical baselines: Tier 2 compares to one
prior-day window, not to weekly/monthly seasonality. If the user
says "is this normal for a Friday afternoon", suggest an anomaly
alert (
signoz-creating-alerts with anomaly_rule).
Guardrails
- Three-tier early-stop is mandatory. Skipping the gates pulls
hundreds of traces/logs on quiet alerts and explodes context. The
gates are not optional optimizations.
- Anchor every claim to an MCP query result. No speculation. If
evidence is missing, lower confidence and say so.
- Show the supporting query with each hypothesis so the user can
reproduce and dig deeper.
- Keep it short and evidence-backed. TL;DR is one or two sentences max; the
full report is a triage card, not a postmortem. Engineers under
pressure should be able to skim the top and act. Every section
earns its place by adding evidence the user couldn't already see
in the alert payload.
- Correlation ≠ causation. Label something a cause only when at
least two of the following converge: temporal precedence (signal
moved before symptom), topology / dependency edge, shared service
or entity, correlated metric/log/trace evidence, or a recent
deploy/config change. A single time-aligned anomaly is a
"co-occurring signal," not a cause; say so explicitly.
- Don't restate the alert or recommend the obvious. "Check
logs", "verify connectivity", "investigate dashboards": the
reader of this output already knows they need to. Replace generic
suggestions with specific queries, traces, or filters they can run
immediately.
- No fabricated identifiers. Trace IDs, span names, alert rule
IDs, channel names, deploy IDs: every identifier in the output
must come from a real MCP response. Don't invent placeholders that
look plausible.
- Report uncertainty honestly. If no hypothesis reaches medium
confidence, the answer is "No clear root cause found; here's
what we checked and what's ruled out." Do not promote a
low-confidence guess to the leading hypothesis just to sound
useful. False positives waste active incident time more than false
negatives.
- Prefer resource-attribute filters in every drill-down query.
This is the SigNoz MCP guideline and it directly affects query
speed at scale.
- Do not modify any alert. Investigate is read-only. If the user
says "and tighten this alert", surface that as a next-step
recommendation; do not call
signoz_update_alert.
- Stay in scope. Static rule explanation belongs to
signoz-explaining-alerts. Cause analysis without an alert anchor
belongs to signoz-generating-queries.
- Time zones. Always state fire windows in UTC alongside relative
time ("14:32 UTC, 2h ago") so autonomous and interactive consumers
agree on the window.
Examples
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 for 8m, single fire (not flapping).
Tier 1: re-runs error-rate formula over [14:02, 15:02]. Peak
error rate 12.4% (vs 5% threshold, 148% over). Pre-fire baseline
0.3%. Real fire, not marginal.
Tier 2: pulls neighbor signals for service.name = checkout:
- p99 latency: 4.1s vs 320ms baseline (+1180%).
- Throughput: -42% (drop).
- Downstream
payments error rate: 18% vs 0.2% baseline (+8900%).
- CPU/memory: flat (no resource pressure).
Tier 3: traces for service.name = checkout, has_error = true
in the fire window: top operation POST /checkout/submit, top
error message "context deadline exceeded calling payments-api".
30 traces, all hitting the same downstream URL. Logs show
matching "payments client timeout" lines, 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… to see the failing call.
- What fired: alert 42 fired 2h ago at 14:32 UTC, sustained
8m. Error rate peaked at 12.4% (148% over threshold).
- Investigation trail:
- ✅ Tier 1: peak 12.4% vs 5% threshold, pre-fire baseline 0.3%.
Real fire.
- ✅ Tier 2: payments error rate +8900%, p99 latency +1180%,
throughput −42%.
- ❌ CPU / memory pressure on checkout: flat.
- ✅ Tier 3: 30 error traces all hit payments-api with
context deadline exceeded; 142 matching timeout logs.
- Likely causes (high confidence): payments service errors
cascading into checkout. Evidence converges across topology
(checkout → payments edge), temporal precedence (payments
errors lead checkout p99), and 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… in the SigNoz UI; if
payments has its own alert, run signoz-explaining-alerts on
it, otherwise signoz-generating-queries for payments error
rate over the last 4h.
User: "Investigate the flapping CPU alert on prod-api-3"
Agent:
Resolves alert (id 88, host.name = prod-api-3).
History: 7 fires in last 1h, alternating fire/resolve every 8-12
minutes: a flapping pattern.
Tier 1: peak 84% (threshold 80%, only 5% over). Each fire
lasted 2-4 minutes. Marginal: the value hovered near threshold.
matchType = at_least_once made each blip trigger.
Early-stop kicks in. Skip tiers 2/3.
Output:
TL;DR: alert 88 has been flapping on prod-api-3: 7 fires
in the last 1h, each 2–4m, all within 5% of the 80% threshold.
No clear root cause; this is threshold tuning, not an incident.
Switch matchType to on_average or add hysteresis to stop
the noise.
- What fired: alert 88, host
prod-api-3, fire pattern
flapping. 7 fires in the last 1h, each 2–4m. Peak 84% (5%
over the 80% threshold).
- Investigation trail:
- ✅ Tier 1: every fire was within 5% of threshold; duration
short; baseline already at 70–75%. Marginal fire; early-stop
triggered, Tier 2/3 skipped.
- Likely causes (low / co-occurring signal only): threshold
tuned too tight or
matchType is too sensitive. Evidence:
every fire was within 5% of threshold; baseline already runs
at 70–75%. Not promoted to a "cause": single signal, no
corroboration.
- Ruled out: real CPU saturation incident (peaks too small
and short-lived; baseline already near threshold).
- Next steps: change
matchType to on_average (smooths
transient spikes) OR raise threshold to 85% with hysteresis
(recoveryTarget: 75). Use signoz_update_alert to apply.
User: "RCA for the log volume alert that fired last night"
Agent:
Resolves alert: "Error Log Volume Spike" (id 14, no service
filter; groupBy service.name).
History: fired at 03:12 UTC, sustained 22m, broke down by service
in the alert annotations: service.name = inventory was the
firing series.
Tier 1: re-runs log count for inventory in fire window. Peak
3,400 errors/min vs 1,000/min threshold (240% over). Pre-fire
baseline 12/min. Real, large fire.
Tier 2: neighbor signals for service.name = inventory:
- Request error rate: +600%.
- p99 latency: +30% (mild).
- CPU: -80% (collapsed). Memory: -60%.
- Pod restarts (k8s): 4 in fire window.
Tier 3: logs for inventory in fire window. Top message: "OOMKilled
restarting" (1,200 occurrences). Top trace error: graceful-shutdown
exceptions.
Output:
TL;DR: log volume alert 14 fired at 03:12 UTC for
service.name = inventory, sustained 22m at 240% over threshold.
Most likely cause: inventory pods OOM-killed and restarted 4
times (high confidence). Check container memory limits for the
inventory deployment.
- What fired: alert 14 fired at 03:12 UTC for service
inventory, sustained 22m, 240% over threshold.
- Investigation trail:
- ✅ Tier 1: peak 3,400 errors/min vs 1,000/min threshold;
pre-fire baseline 12/min. Real fire.
- ✅ Tier 2: request error rate +600%; CPU/memory collapsed
(−80%/−60%); 4 pod restarts in window.
- ❌ p99 latency: only +30%, not a latency-driven incident.
- ✅ Tier 3: top log message "OOMKilled restarting" (1,200
occurrences); top trace error: graceful-shutdown exceptions.
- Likely causes (high confidence): inventory pods OOM-killed
and restarted 4 times during the window. Evidence converges
across topology (single service), temporal precedence (memory
fell to zero before error spike), shared entity (all log lines
from
service.name = inventory), and a single coherent
pattern (OOM → restart → graceful-shutdown noise).
- Ruled out: a true application error-rate change (errors
are restart noise, not request-path failures); upstream
traffic surge (throughput unchanged).
- Next steps: check container memory limits for inventory
pods; review recent deploys; consider whether the alert should
exclude restart-related error patterns or whether the
underlying OOM is the real concern.
Additional resources
references/neighbor-signals.md: lookup table mapping resource type
(service / host / k8s) to the neighbor signals to pull in Tier 2.
references/baseline-comparison.md: query templates that pair
fire-window and baseline-window calls cleanly, including how to
format signoz_execute_builder_query for both.
signoz-explaining-alerts skill: to decode the rule before
investigating, if the user is unfamiliar with what the alert
monitors.
signoz-generating-queries skill: for ad-hoc follow-up queries on the same
resource 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. Make sure to use this skill 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. The skill correlates the alert's own9signal with neighbor signals around the fire window, and surfaces a10ranked list of likely causes with supporting evidence. It is the11companion to `signoz-explaining-alerts`: explain decodes the rule12statically; investigate diagnoses a specific incident.1314## Prerequisites1516This skill calls SigNoz MCP server tools heavily (`signoz_get_alert`,17`signoz_get_alert_history`, `signoz_execute_builder_query`,18`signoz_query_metrics`, `signoz_search_traces`, `signoz_search_logs`,19`signoz_get_trace_details`, etc.). Before running the workflow,20confirm the `signoz_*` tools are available. If they are not, the21SigNoz MCP server is not installed or configured; run `signoz-mcp-setup` first22to initialize or repair the MCP connection. The investigation depends on23correlating multiple MCP queries; without the server there is no way to ground24the analysis.2526## When to use2728Use this skill when the user wants to:29- Understand why a specific alert fired.30- Find the root cause of a recent incident triggered by an alert.31- Correlate the alert's signal with related metrics, traces, and logs.32- Distinguish "real signal" fires from flapping or threshold-mistuning.3334Do NOT use when the user wants to:35- Understand what an alert is configured to monitor → `signoz-explaining-alerts`.36- Create a new alert → `signoz-creating-alerts`.37- Modify an alert (raise threshold, add hysteresis) → call38 `signoz_update_alert` directly.39- Run a free-form ad-hoc investigation without an alert as the anchor →40 `signoz-generating-queries`.4142## Required inputs4344| Input | Required | Source if missing |45|---|---|---|46| Alert identifier (rule ID or name) | yes | `$ARGUMENTS[0]` or recent context |47| Time window | no | default to most recent fire from `signoz_get_alert_history` |4849If the alert name is fuzzy, this skill is **best-effort** (read-only):501. Call `signoz_list_alert_rules`, paginate, fuzzy-match the name.512. State the interpretation: "Investigating fire of 'High Error Rate -52 Checkout' (id 42) at 14:32 UTC. If you meant a different alert or53 fire, tell me."543. Proceed.5556If no firing transition exists in the queried lookback window, **stop**: there is57nothing to investigate. Respond with:58> "Alert '[name]' has not fired in the last 7d, so there is no fire59> window to investigate. Use `signoz-explaining-alerts` to walk through60> the rule, or check whether the alert is enabled."6162## Workflow6364The investigation runs in three tiers with strict early-stop gates.65Tier 1 always runs. Tier 2 runs only if tier 1 confirms a real fire.66Tier 3 runs only if tier 2 surfaces correlated anomalies. Skipping the67gates produces hundreds of unnecessary trace/log queries on quiet68alerts.6970### Step 1: Resolve alert + fire window (Tier 0)71721. Resolve the alert id via `signoz_list_alert_rules` (paginated) if73 not given.742. Call `signoz_get_alert` for the full rule config, needed to know75 what query, threshold, and resource scope the alert evaluated.763. First call `signoz_get_alert_history` with `timeRange: "7d"` and77 `order: "desc"`; omit `state` so the timeline includes firing and inactive78 transitions. Continue only when `data.nextCursor` exists (the completeness79 note also reports `hasMore: true`). Pass it as `cursor`, replace `timeRange`80 with the note's resolved absolute `start` and `end`, and preserve the same81 state/filter (including omission) and order. Stop when `nextCursor` is absent82 / the note reports `hasMore: false`; never use `offset` or page fullness.83 If a later intentional state filter means "resolved" / "recovered", use84 `inactive`. The enum is `inactive|pending|recovering|firing|nodata|disabled`;85 `recovering` is a transient keep-firing state, not resolution.86 Pattern analysis needs the complete transition set. Rows are emitted per87 label-group `fingerprint`; do not interleave them. From the response:88 - **Build rule-wide incident windows** from distinct rows where89 `overallStateChanged: true`: an `overallState: "firing"` transition opens90 an incident; the next `overallState: "inactive"` closes it. Deduplicate91 matching timestamps and sort by `unixMilli` ascending before pairing.92 Default to the most recent incident unless `$ARGUMENTS[1]` selects another.93 - **Partition affected series by `fingerprint`** and retain each row's94 labels. Use only `stateChanged: true` rows to decide when that group fired95 and resolved, and which group should scope Tier 1–3 queries.96 - **Note the fire pattern** from rule-wide transitions or one named fingerprint:97 - `one-off` → single fire with a long quiet period before/after.98 - `sustained` → fires that stayed firing for ≥ 1 evaluation cycle.99 - `flapping` → ≥ 3 fires within a 1h window, alternating fire/resolve.100 - `recurring` → fires at regular intervals (cron-like, e.g., every hour).101 - Never infer flapping from different fingerprints. The pattern guides tiers 2/3.102103### Step 2: Tier 1 (what fired and how hard)104105This tier always runs. It establishes the fire is real (vs. transient106threshold tickle or flap) and quantifies the magnitude.1071081. Re-run the alert's primary query over a window centered on the fire109 start: `[fire_start - 30m, fire_start + 30m]`.110 - Use `signoz_execute_builder_query` for the alert's stored builder,111 formula, PromQL, or ClickHouse query envelope.112 - 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.113 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.114 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`.115 Time-series top-N ranks over the whole window and may omit a short-lived local spike.1162. Compute:117 - **Peak value** during the fire window.118 - **Threshold breach magnitude**: `(peak - threshold) / threshold *119 100` for "above" alerts, inverted for "below".120 - **Fire duration**: the rule's overall firing→inactive interval, or the121 selected fingerprint's interval for a group-scoped investigation. Say which.122 - **Pre-fire baseline**: average value in the 30m before fire start.1233. **Early-stop gate**: if the breach magnitude is < 10% over the124 threshold AND the fire duration is < 1 evaluation window, classify125 as "marginal fire"; the alert may be too sensitive. Skip tiers 2126 and 3 and go to Step 5 with a single hypothesis: "threshold may be127 too tight, recommend tuning."128129### Step 3: Tier 2 (neighbor signals vs baseline)130131Run only if Tier 1 confirms a real breach. Pull related signals for the132same resource scope as the alert and compare the fire window to a133baseline window.1341351. **Pick a baseline window**. Use the same hour, previous day136 (`fire_start - 24h, fire_start - 24h + fire_duration`). If the137 alert fired during a known-anomalous time (deploy, weekly job),138 note it in the output but still proceed.1391402. **Look up neighbor signals** for the alert's resource type. See141 `references/neighbor-signals.md` for the lookup table. Common cases:142 - **Service-level alert** (`service.name = X`): pull error rate,143 p95/p99 latency, request throughput, dependency error rates if144 trace data is available.145 - **Host / VM alert** (`host.name = X`): CPU, memory, disk I/O,146 network I/O.147 - **K8s pod / namespace alert**: pod restarts, container CPU/memory148 limits, node pressure, recent rollouts.1491503. For each neighbor signal:151 - Query both windows (fire + baseline) via152 `signoz_execute_builder_query` or `signoz_query_metrics`.153 - Compute the delta (% change in fire window vs baseline).154 - Rank by absolute delta.1551564. **Early-stop gate**: if no neighbor signal shows ≥ 25% deviation157 from baseline, classify as "isolated fire: the alert's own signal158 moved but nothing else did." This is unusual and worth surfacing.159 Skip Tier 3 and go to Step 5 with hypotheses focused on the alert's160 own query (likely causes: data source change, instrumentation161 change, downstream silent failure that only shows in this metric).162163### Step 4: Tier 3 (traces and logs at the fire window)164165Run only if Tier 2 found correlated neighbor anomalies. Drill into166specific failing operations.1671681. **Traces** (if the alert is service-scoped and traces are169 available):170 - Call `signoz_search_traces` for the fire window with filter:171 `service.name = <scope>` AND `has_error = true`. Cap at top 20.172 - Group by `name` and `status_message`. Surface the sample's top 3 with one173 trace ID each; do not call a 20-row sample count full-window frequency.174 - Optionally call `signoz_get_trace_details` for span attributes. Pass the175 search row's `trace_id` as `traceId` **plus the same absolute fire-window176 `start` and `end`**; otherwise the 6h default misses older incidents.1771782. **Logs** for the fire window:179 - Call `signoz_search_logs` with filter:180 `<scope_filter>` AND `severity_text IN ('ERROR', 'FATAL')`. Cap181 at top 20 most recent.182 - Group by `body` pattern (or `exception.type` if present). Surface183 the top 3 distinct messages with counts.1841853. Cross-reference: do the traces and logs point at the same186 downstream service, dependency, or code path? If so, that becomes187 the leading hypothesis.188189See `references/baseline-comparison.md` for query templates that pair190fire-window and baseline-window calls cleanly.191192### Step 5: Build the structured output193194Use this exact section order. Lead with a TL;DR, because engineers195under pressure scan the top first and stop reading once they have what196they need. Every claim cites the MCP query that produced it, with no197generic "check logs / verify connectivity" filler.198199**1. TL;DR**: one or two sentences, no more. Leading hypothesis,200overall confidence, blast radius, and the single most useful next201action. Example:202> "checkoutservice error rate hit 12.4% (threshold 5%) for 8m at203> 14:32 UTC; most likely cause is payments-api timing out204> (high confidence). Open trace `7af3a09b…` to see the failing call."205206If no hypothesis reaches medium confidence, the leading line is207"No clear root cause found." rather than a low-confidence guess208dressed up as the answer.209210**2. What fired**211The alert (id, name), the fire window (absolute UTC + relative),212peak magnitude ("error rate hit 12.4% vs. 5% threshold, 148% over"),213fire duration, and the fire pattern (`one-off` / `sustained` /214`flapping` / `recurring` / `marginal`).215216**3. Investigation trail**217A scannable list of what was checked, with ✅ for confirmed signals218and ❌ for ruled out, each followed by a one-line finding. The point219is that the reader can see what work the AI did and what it found.220Example:221- ✅ Tier 1: peak error rate 12.4%, fire was real (not marginal).222- ✅ Tier 2: payments error rate +8900%, p99 +1180%; downstream223 cascade.224- ❌ CPU / memory pressure: flat through the fire window.225- ✅ Tier 3: 30 error traces all hit payments-api, same message.226227**4. Likely causes** (ranked, max 3)228Each cause has three parts:229- **Hypothesis**: one sentence, specific. Bad: "service is unhealthy".230 Good: "checkout is timing out on calls to payments-api".231- **Evidence**: the supporting numbers from tiers 1/2/3, with the232 underlying query inline so the user can re-run it. State the233 neighbor signal, the delta vs baseline, the trace/log pattern that234 supports it.235- **Confidence**: `high` requires ≥2 of: temporal precedence,236 topology / dependency edge, shared service or entity, correlated237 metric/log/trace evidence, recent deploy or config change.238 `medium` is one tier's evidence with at least one of those239 signals. `low` is a single signal moved with no corroboration;240 in that case label it a "co-occurring signal," not a cause.241242If only Tier 1 ran (marginal fire / no neighbor anomalies), output243fewer hypotheses with `low` confidence and explicitly call out the244limitation.245246**5. Ruled out**247Short but explicit. List candidates the evidence eliminated and the248one-line reason why. Skip the section if there's nothing meaningful249to rule out, but if you considered something and dropped it, say so250here so the user doesn't waste time re-checking it.251252**6. Suggested next steps**253Action items the user can take. Be concrete and use SigNoz-native254handles so the user can act immediately:255- Specific trace, dashboard, or alert to open256 (e.g., "open trace `7af3a09b…` in the SigNoz UI").257- Specific query to run with `signoz-generating-queries`: paste258 the exact filter and time window.259- "Tune this alert" if the fire was marginal: name the field260 (`matchType`, `target`, `recoveryTarget`) and the change to make261 via `signoz_update_alert`.262- "Open an incident" or "page the owning team" if the cause is263 cross-service.264265Do not pad with generic advice ("verify connectivity", "check266dashboards"); that's noise during an active incident.267268**Mirroring as navigation chips.** Mirror up to 3 of these "Suggested269next steps" as host follow-up intents: the most actionable,270alert-scoped ones. Keep the rest in the report prose so the user has271the full picture. The chip surface is capped; the prose is not.272273## Out of scope (v1)274275- **Deployment / config-change correlation**: SigNoz MCP does not276 expose a deployments tool; do not fabricate one. If the user277 mentions a recent deploy, surface it as context but don't claim it278 caused the fire without the signal evidence.279- **Cross-service blast-radius walking**: investigating downstream280 callers of the alert's service. Out of scope to keep context281 bounded.282- **Long-horizon historical baselines**: Tier 2 compares to one283 prior-day window, not to weekly/monthly seasonality. If the user284 says "is this normal for a Friday afternoon", suggest an anomaly285 alert (`signoz-creating-alerts` with `anomaly_rule`).286287## Guardrails288289- **Three-tier early-stop is mandatory.** Skipping the gates pulls290 hundreds of traces/logs on quiet alerts and explodes context. The291 gates are not optional optimizations.292- **Anchor every claim to an MCP query result.** No speculation. If293 evidence is missing, lower confidence and say so.294- **Show the supporting query** with each hypothesis so the user can295 reproduce and dig deeper.296- **Keep it short and evidence-backed.** TL;DR is one or two sentences max; the297 full report is a triage card, not a postmortem. Engineers under298 pressure should be able to skim the top and act. Every section299 earns its place by adding evidence the user couldn't already see300 in the alert payload.301- **Correlation ≠ causation.** Label something a cause only when at302 least two of the following converge: temporal precedence (signal303 moved before symptom), topology / dependency edge, shared service304 or entity, correlated metric/log/trace evidence, or a recent305 deploy/config change. A single time-aligned anomaly is a306 "co-occurring signal," not a cause; say so explicitly.307- **Don't restate the alert or recommend the obvious.** "Check308 logs", "verify connectivity", "investigate dashboards": the309 reader of this output already knows they need to. Replace generic310 suggestions with specific queries, traces, or filters they can run311 immediately.312- **No fabricated identifiers.** Trace IDs, span names, alert rule313 IDs, channel names, deploy IDs: every identifier in the output314 must come from a real MCP response. Don't invent placeholders that315 look plausible.316- **Report uncertainty honestly.** If no hypothesis reaches medium317 confidence, the answer is "No clear root cause found; here's318 what we checked and what's ruled out." Do not promote a319 low-confidence guess to the leading hypothesis just to sound320 useful. False positives waste active incident time more than false321 negatives.322- **Prefer resource-attribute filters** in every drill-down query.323 This is the SigNoz MCP guideline and it directly affects query324 speed at scale.325- **Do not modify any alert.** Investigate is read-only. If the user326 says "and tighten this alert", surface that as a next-step327 recommendation; do not call `signoz_update_alert`.328- **Stay in scope.** Static rule explanation belongs to329 `signoz-explaining-alerts`. Cause analysis without an alert anchor330 belongs to `signoz-generating-queries`.331- **Time zones.** Always state fire windows in UTC alongside relative332 time ("14:32 UTC, 2h ago") so autonomous and interactive consumers333 agree on the window.334335## Examples336337**User:** "Why did the checkout error rate alert fire?"338339**Agent:**3401. Resolves alert: "High Error Rate - Checkout" (id 42).3412. `signoz_get_alert_history` → most recent fire 2h ago at 14:32 UTC,342 sustained for 8m, single fire (not flapping).3433. **Tier 1**: re-runs error-rate formula over `[14:02, 15:02]`. Peak344 error rate 12.4% (vs 5% threshold, 148% over). Pre-fire baseline345 0.3%. Real fire, not marginal.3464. **Tier 2**: pulls neighbor signals for `service.name = checkout`:347 - p99 latency: 4.1s vs 320ms baseline (+1180%).348 - Throughput: -42% (drop).349 - Downstream `payments` error rate: 18% vs 0.2% baseline (+8900%).350 - CPU/memory: flat (no resource pressure).3515. **Tier 3**: traces for `service.name = checkout, has_error = true`352 in the fire window: top operation `POST /checkout/submit`, top353 error message "context deadline exceeded calling payments-api".354 30 traces, all hitting the same downstream URL. Logs show355 matching "payments client timeout" lines, 142 occurrences.3566. **Output**:357358 > **TL;DR**: checkoutservice error rate hit 12.4% (threshold 5%)359 > for 8m at 14:32 UTC. Most likely cause: payments-api timing out360 > (high confidence; converging trace + log + neighbor evidence).361 > Open trace `7af3a09b…` to see the failing call.362363 - **What fired**: alert 42 fired 2h ago at 14:32 UTC, sustained364 8m. Error rate peaked at 12.4% (148% over threshold).365 - **Investigation trail**:366 - ✅ Tier 1: peak 12.4% vs 5% threshold, pre-fire baseline 0.3%.367 Real fire.368 - ✅ Tier 2: payments error rate +8900%, p99 latency +1180%,369 throughput −42%.370 - ❌ CPU / memory pressure on checkout: flat.371 - ✅ Tier 3: 30 error traces all hit payments-api with372 `context deadline exceeded`; 142 matching timeout logs.373 - **Likely causes** (high confidence): payments service errors374 cascading into checkout. Evidence converges across topology375 (checkout → payments edge), temporal precedence (payments376 errors lead checkout p99), and shared entity (every error377 trace targets the payments-api URL).378 - **Ruled out**: checkout-side resource pressure (CPU/memory379 flat); upstream traffic spike (throughput dropped, didn't380 spike).381 - **Next steps**: open trace `7af3a09b…` in the SigNoz UI; if382 payments has its own alert, run `signoz-explaining-alerts` on383 it, otherwise `signoz-generating-queries` for payments error384 rate over the last 4h.385386---387388**User:** "Investigate the flapping CPU alert on prod-api-3"389390**Agent:**3911. Resolves alert (id 88, host.name = prod-api-3).3922. History: 7 fires in last 1h, alternating fire/resolve every 8-12393 minutes: a flapping pattern.3943. **Tier 1**: peak 84% (threshold 80%, only 5% over). Each fire395 lasted 2-4 minutes. Marginal: the value hovered near threshold.396 `matchType = at_least_once` made each blip trigger.3974. Early-stop kicks in. Skip tiers 2/3.3985. **Output**:399400 > **TL;DR**: alert 88 has been flapping on `prod-api-3`: 7 fires401 > in the last 1h, each 2–4m, all within 5% of the 80% threshold.402 > No clear root cause; this is threshold tuning, not an incident.403 > Switch `matchType` to `on_average` or add hysteresis to stop404 > the noise.405406 - **What fired**: alert 88, host `prod-api-3`, fire pattern407 `flapping`. 7 fires in the last 1h, each 2–4m. Peak 84% (5%408 over the 80% threshold).409 - **Investigation trail**:410 - ✅ Tier 1: every fire was within 5% of threshold; duration411 short; baseline already at 70–75%. Marginal fire; early-stop412 triggered, Tier 2/3 skipped.413 - **Likely causes** (low / co-occurring signal only): threshold414 tuned too tight or `matchType` is too sensitive. Evidence:415 every fire was within 5% of threshold; baseline already runs416 at 70–75%. Not promoted to a "cause": single signal, no417 corroboration.418 - **Ruled out**: real CPU saturation incident (peaks too small419 and short-lived; baseline already near threshold).420 - **Next steps**: change `matchType` to `on_average` (smooths421 transient spikes) OR raise threshold to 85% with hysteresis422 (`recoveryTarget: 75`). Use `signoz_update_alert` to apply.423424---425426**User:** "RCA for the log volume alert that fired last night"427428**Agent:**4291. Resolves alert: "Error Log Volume Spike" (id 14, no service430 filter; groupBy `service.name`).4312. History: fired at 03:12 UTC, sustained 22m, broke down by service432 in the alert annotations: `service.name = inventory` was the433 firing series.4343. **Tier 1**: re-runs log count for inventory in fire window. Peak435 3,400 errors/min vs 1,000/min threshold (240% over). Pre-fire436 baseline 12/min. Real, large fire.4374. **Tier 2**: neighbor signals for `service.name = inventory`:438 - Request error rate: +600%.439 - p99 latency: +30% (mild).440 - CPU: -80% (collapsed). Memory: -60%.441 - Pod restarts (k8s): 4 in fire window.4425. **Tier 3**: logs for inventory in fire window. Top message: "OOMKilled443 restarting" (1,200 occurrences). Top trace error: graceful-shutdown444 exceptions.4456. **Output**:446447 > **TL;DR**: log volume alert 14 fired at 03:12 UTC for448 > `service.name = inventory`, sustained 22m at 240% over threshold.449 > Most likely cause: inventory pods OOM-killed and restarted 4450 > times (high confidence). Check container memory limits for the451 > inventory deployment.452453 - **What fired**: alert 14 fired at 03:12 UTC for service454 `inventory`, sustained 22m, 240% over threshold.455 - **Investigation trail**:456 - ✅ Tier 1: peak 3,400 errors/min vs 1,000/min threshold;457 pre-fire baseline 12/min. Real fire.458 - ✅ Tier 2: request error rate +600%; CPU/memory collapsed459 (−80%/−60%); 4 pod restarts in window.460 - ❌ p99 latency: only +30%, not a latency-driven incident.461 - ✅ Tier 3: top log message "OOMKilled restarting" (1,200462 occurrences); top trace error: graceful-shutdown exceptions.463 - **Likely causes** (high confidence): inventory pods OOM-killed464 and restarted 4 times during the window. Evidence converges465 across topology (single service), temporal precedence (memory466 fell to zero before error spike), shared entity (all log lines467 from `service.name = inventory`), and a single coherent468 pattern (OOM → restart → graceful-shutdown noise).469 - **Ruled out**: a true application error-rate change (errors470 are restart noise, not request-path failures); upstream471 traffic surge (throughput unchanged).472 - **Next steps**: check container memory limits for inventory473 pods; review recent deploys; consider whether the alert should474 exclude restart-related error patterns or whether the475 underlying OOM is the real concern.476477## Additional resources478479- `references/neighbor-signals.md`: lookup table mapping resource type480 (service / host / k8s) to the neighbor signals to pull in Tier 2.481- `references/baseline-comparison.md`: query templates that pair482 fire-window and baseline-window calls cleanly, including how to483 format `signoz_execute_builder_query` for both.484- `signoz-explaining-alerts` skill: to decode the rule before485 investigating, if the user is unfamiliar with what the alert486 monitors.487- `signoz-generating-queries` skill: for ad-hoc follow-up queries on the same488 resource scope.