Skip to main content
Glama

Save Alert Rule

save_alert_rule
Destructive

Create or update an alert rule for the calling tenant in one call. WRITE: available to any authenticated user. Omit ruleId to create a new rule; supply ruleId to REPLACE an existing one.

UPDATE IS A WHOLE-OBJECT REPLACE, NOT A MERGE. Every field you leave out is cleared — omitting description sets it to null. To change one thing, fetch the rule with get_alert_rules and re-send its full spec with that one field altered. Two things are carved out and survive omission: status — ACTIVE/DISABLED is preserved; change it with set_alert_rule_status delivery — notifyOnResolve is preserved. Where a rule's alerts go is not part of the rule spec: change it with set_alert_rule_destinations, and read it with get_alert_rule_destinations.

channelIds applies on CREATE only (ADMIN only): the new rule's notification destinations as registry channel UUIDs from the investigation service's list_alert_channels. Omit to inherit the tenant's alerting default; pass [] to create the rule with its alerts off. Sending channelIds together with ruleId is rejected in problems[]; use set_alert_rule_destinations to change an existing rule's destinations.

Re-validates exactly like preview_alert_rule: if the spec is invalid, nothing is persisted and problems[] is populated instead of rule — preview_alert_rule first to calibrate the threshold, then save once problems[] is empty there.

The rule still saves even when warnings[] is non-empty — warnings are advisory, never a reason to withhold saving, unlike problems[]. warnings[] currently carries one code, FIELD_NEVER_OBSERVED: a filter/groupBy field querysql couldn't resolve to a known column (so it silently falls back to reading it from the JSON catch-all) and that has never appeared in this customer's recent telemetry — almost always a typo'd field name, especially when preview_alert_rule also reported dataCoverage.status = NO_MATCHING_DATA. Fix the spelling and re-preview rather than treat it as a calibration problem.

Authors a single metric or anomaly rule (one measure over a rolling window). Compound multi-condition rules can't be created here — build those in the web editor.

METRIC RULE (structured) — watches one measure over a rolling time window: source: telemetry source (required): LOGS, SPANS, METRICS filter: optional QuerySQL boolean filter, e.g. service = 'my-svc' fn: catalog measure function, e.g. count, error_rate, p95, error_burn_rate arg: optional field the measure operates on, e.g. duration_ms for p95 params: optional named measure params, e.g. {"budget":"0.001"} (error_burn_rate) expression: optional free-form aggregate (used instead of fn) — a ratio/calculation, e.g. countIf(status_code = 'ERROR') * 100.0 / count() (this is exactly fn: error_rate; use fn instead unless you need a custom ratio — both already return 0-100, don't divide by 100 again) metricName/metricType: required only when source is METRICS unit: optional explicit display unit for the measure, e.g. BYTES or DURATION_MS — set it when the metric name doesn't self-describe its unit (OTel names like jvm.memory.used or http.server.request.duration carry no unit suffix); omit it to let the server infer the unit from the metric name or measure function windowMinutes: rolling window length in minutes (required) groupBy: optional list of plain field names to group the series by, e.g. service. Field names only: a string that looks like an expression (it contains a parenthesis or a space) is rejected in problems[], because the rule page and the alert title would then show the raw expression as the group name. derivedGroupBy: optional list of computed group keys, each an object {"expression": "...", "label": "..."}, e.g. {"expression": "regexp_extract(message, 'customerId=([0-9a-f-]+)', 1)", "label": "customer"}. Both fields are required and non-blank. The label is what rule pages and alert titles show, so give it a short readable name. groupBy and derivedGroupBy can be used together. comparator: threshold comparator: GT, GTE, LT, LTE (required for static) warningThreshold: the warning-tier threshold the measure is compared against (required for static) warningConsecutiveWindows: consecutive breaching windows for the warning tier (default 1) criticalThreshold / criticalConsecutiveWindows: optional escalation tier

ANOMALY METRIC (structured) — flags a measure that deviates from its own historical baseline instead of a fixed threshold. Supply zScoreThreshold + direction instead of comparator/warningThreshold; groupBy must be empty. zScoreThreshold: robust z-score magnitude that counts as anomalous (> 0) direction: HIGH (spikes above baseline) or LOW (drops below baseline) anomalyConsecutiveWindows: consecutive anomalous windows required (>= 1)

Common fields: name: human-readable rule name (required, non-blank) description: optional; shown with every alert this rule raises, read during triage. One or two short sentences: what the breach means for users, then the first thing to check. E.g. "Search is slow for users. Check the search-index cluster first." Leave out backtest or backfill numbers, calibration history, incident history, dates and runbook steps. notifyOnResolve: whether to notify when the alert resolves (default true) active: create-only — whether the rule starts ACTIVE (default true) or DISABLED

Input Schema

NameRequiredDescriptionDefault
fnNoCatalog measure function: count, error_rate, p95, error_burn_rate, ...
argNoOptional field the measure operates on, e.g. duration_ms
nameYesHuman-readable rule name (non-blank)
unitNoExplicit display unit for the measure, e.g. BYTES or DURATION_MS — set it when the metric name doesn't self-describe its unit (OTel names like jvm.memory.used or http.server.request.duration carry no unit suffix); omit to let the server infer the unit from the metric name or measure function
checkNoCompound AND/OR check tree as JSON, in the same shape get_alert_rules returns in checkDetails.check. Supply instead of the structured measurement and condition fields, which are ignored when this is set.
activeNoCreate-only: start the rule ACTIVE (default true) or DISABLED
filterNoOptional QuerySQL boolean filter, e.g. service = 'my-svc'
paramsNoOptional named measure params, e.g. {"budget":"0.001"}
ruleIdNoRule id (UUID) to update; omit to create a new rule
sourceNoTelemetry source: LOGS, SPANS, METRICS
groupByNoOptional plain field names to group the series by, e.g. service. Field names only: a string that looks like an expression is rejected, pass it in derivedGroupBy instead
directionNoAnomaly direction: HIGH or LOW
channelIdsNoCreate-only, ADMIN-only: notification destinations as registry channel UUIDs (the id field from the investigation service's list_alert_channels, not Slack channel ids). Omit to inherit the tenant's alerting default; [] creates the rule with its alerts off
comparatorNoThreshold comparator: GT, GTE, LT, LTE (static rules)
expressionNoOptional free-form aggregate expression measuring the source, used instead of fn (takes precedence when set). QuerySQL over the source's fields, e.g. a ratio 'countIf(status_code = ''ERROR'') * 100.0 / count()' (this is exactly fn: error_rate, which already returns 0-100 — don't divide by 100 again) or a metric ratio 'avg(if(metric_name = ''a'', value, null)) / avg(if(metric_name = ''b'', value, null))'.
metricNameNoMetric name (required only when source is METRICS)
metricTypeNoMetric type: GAUGE, SUM, HISTOGRAM, ... (only when source is METRICS)
descriptionNoOptional; shown with every alert this rule raises, read during triage. One or two short sentences: what the breach means for users, then the first thing to check, e.g. 'Search is slow for users. Check the search-index cluster first.' Leave out backtest or backfill numbers, calibration history, incident history, dates and runbook steps.
windowMinutesNoRolling window length in minutes
derivedGroupByNoOptional computed group keys, each an object with an expression and a label, e.g. {"expression": "regexp_extract(message, 'customerId=([0-9a-f-]+)', 1)", "label": "customer"}. The label is what rule pages and alert titles show
notifyOnResolveNoNotify when the alert resolves (default true)
zScoreThresholdNoAnomaly z-score threshold (> 0) — supply instead of comparator/warningThreshold
warningThresholdNoWarning-tier threshold (static rules)
criticalThresholdNoOptional critical-tier threshold (escalation)
anomalyConsecutiveWindowsNoConsecutive anomalous windows required (>= 1)
warningConsecutiveWindowsNoConsecutive breaching windows for the warning tier (default 1)
criticalConsecutiveWindowsNoConsecutive breaching windows for the critical tier (default 1)

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedInput schema / properties / description / description
      Previous value: -"Optional free-text description"New value: +"Optional; shown with every alert this rule raises, read during triage. One or two short sentences: what the breach means for users, then the first thing to check, e.g. 'Search is slow for users. Check the search-index cluster first.' Leave out backtest or backfill numbers, calibration history, incident history, dates and runbook steps."
  2. Changed9 schema fields changed
    • addedInput schema / $schema
      Added value: +"https://json-schema.org/draft/2020-12/schema"
    • removedInput schema / additionalProperties
      Removed value: -false
    • addedInput schema / properties / anomalyConsecutiveWindows / format
      Added value: +"int32"
    • addedInput schema / properties / criticalConsecutiveWindows / format
      Added value: +"int32"
    • addedInput schema / properties / criticalThreshold / format
      Added value: +"double"
    • addedInput schema / properties / warningConsecutiveWindows / format
      Added value: +"int32"
    • addedInput schema / properties / warningThreshold / format
      Added value: +"double"
    • addedInput schema / properties / windowMinutes / format
      Added value: +"int32"
    • addedInput schema / properties / zScoreThreshold / format
      Added value: +"double"
  3. Changed1 schema field changed
    • addedInput schema / properties / channelIds
      Added value: +{
      +  "description": "Create-only, ADMIN-only: notification destinations as registry channel UUIDs (the id field from the investigation service's list_alert_channels, not Slack channel ids). Omit to inherit the tenant's alerting default; [] creates the rule with its alerts off",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
  4. Added
  5. Removed
  6. Changed2 schema fields changed
    • addedInput schema / properties / derivedGroupBy
      Added value: +{
      +  "description": "Optional computed group keys, each an object with an expression and a label, e.g. {\"expression\": \"regexp_extract(message, 'customerId=([0-9a-f-]+)', 1)\", \"label\": \"customer\"}. The label is what rule pages and alert titles show",
      +  "items": {
      +    "properties": {
      +      "expression": {
      +        "description": "QuerySQL expression whose value the series are grouped by, e.g. regexp_extract(message, 'customerId=([0-9a-f-]+)', 1)",
      +        "type": "string"
      +      },
      +      "label": {
      +        "description": "Short readable name for the expression, e.g. customer. This is what rule pages and alert titles show, never the expression itself.",
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "expression",
      +      "label"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • changedInput schema / properties / groupBy / description
      Previous value: -"Optional fields to group the series by"New value: +"Optional plain field names to group the series by, e.g. service. Field names only: a string that looks like an expression is rejected, pass it in derivedGroupBy instead"
  7. Changed1 schema field changed
    • removedInput schema / properties / channelIds
      Removed value: -{
      -  "description": "Create-only: registry channel UUIDs (the id field from list_alert_channels, not a Slack/provider channel id) to route this rule's alerts to; omit to inherit the tenant's default channel. Use set_alert_rule_delivery to reroute an existing rule.",
      -  "items": {
      -    "type": "string"
      -  },
      -  "type": "array"
      -}
  8. Changed1 schema field changed
    • addedInput schema / properties / check
      Added value: +{
      +  "description": "Compound AND/OR check tree as JSON, in the same shape get_alert_rules returns in checkDetails.check. Supply instead of the structured measurement and condition fields, which are ignored when this is set.",
      +  "type": "string"
      +}
  9. Changed1 schema field changed
    • addedInput schema / properties / unit
      Added value: +{
      +  "description": "Explicit display unit for the measure, e.g. BYTES or DURATION_MS — set it when the metric name doesn't self-describe its unit (OTel names like jvm.memory.used or http.server.request.duration carry no unit suffix); omit to let the server infer the unit from the metric name or measure function",
      +  "type": "string"
      +}
  10. First observed

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes far beyond the annotations (destructiveHint=true, readOnlyHint=false). It discloses the whole-object replace semantics (omitted fields are cleared, including description set to null), the status/delivery carve-outs that survive omission, the ADMIN-only create-only channelIds behavior and its rejection when combined with ruleId, and the problems[]-vs-warnings[] contract including the FIELD_NEVER_OBSERVED code. This is exactly the non-obvious mutation behavior an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the create/update decision and the replace warning before the field-level detail, and organized under clear headers. It is long, and several blocks (unit, description, expression) are near-verbatim repetitions of the schema, but the size is defensible for a 27-parameter tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 27-parameter, nested-object, no-output-schema mutation tool, the description covers the safe-call workflow, the create/update/anomaly/static mode selection, the validation and error/success contracts (problems[] vs warnings[] vs rule), and the routing to sibling tools. Nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds genuine cross-field and precedence semantics the schema does not: expression takes precedence over fn, groupBy rejects expression-like strings (pass them in derivedGroupBy), derivedGroupBy requires both expression and label, and channelIds is create-only/ADMIN-only. It largely mirrors the schema for several fields rather than extending them, so it falls short of a clean 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource+scope: 'Create or update an alert rule for the calling tenant in one call.' It explicitly distinguishes itself from siblings by naming set_alert_rule_status, set_alert_rule_destinations, get_alert_rules and preview_alert_rule, and it states what it cannot do (compound multi-condition rules). An agent can identify and route to this tool without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use branching (omit ruleId to create, supply ruleId to replace), tells the agent to preview_alert_rule first and re-send full specs after fetching with get_alert_rules, and names the alternative tools for status/destination changes. It also states an exclusion: compound rules must be built in the web editor.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources