> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getmcpulse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Alert rules

> A rule per MCP, scoped to a tool and a client, evaluated once a night against one day of data.

`/m/:id/alerts` holds the rules for one server.

A rule is a metric, a comparator, a threshold and a scope. It is evaluated once a night against the previous day, and where it trips it delivers to everyone on the account whose accepted [channels](/alerts/channels) overlap the rule's.

## Scope is the whole design

A rule narrows by exactly the two dimensions `tool_hours` is keyed by: **a tool** and **a client**. Either may be left as *every*.

That is deliberate, and it is where the value is. Consider two rules on the same server, the same metric and the same day:

| Rule                                                 | Fires?                                 |
| ---------------------------------------------------- | -------------------------------------- |
| First-call on `search_orders`, for Cursor, below 60% | **Yes — 19%**                          |
| First-call server-wide, below 70%                    | No — the average across clients is 81% |

One rule finds the problem; the other cannot see it. Both ship on the sample MCP every new account is seeded with, for exactly that contrast.

A rule can only ask a question a panel can answer, which is the other half of the reasoning: a rule you cannot check on screen is a rule nobody trusts.

## The metrics

A closed vocabulary, not free SQL. Every value is a ratio or a count derivable from one day of `tool_hours` rows, which keeps evaluation a single grouped read.

| Metric               | Unit                         |
| -------------------- | ---------------------------- |
| `first_call_rate`    | %                            |
| `error_rate`         | %                            |
| `empty_rate`         | %                            |
| `crash_rate`         | %                            |
| `slow_rate`          | % — the share over 2 seconds |
| `avg_response_bytes` | bytes                        |
| `calls`              | count                        |

`GET /v1/mcps/:id/alerts` returns this catalogue alongside the rules, and each metric carries **the unit its threshold is expressed in** and the direction that makes sense for it.

That is not decoration. A rate is stored as `0.6` and typed as `60`, so a client that does not know the unit writes a rule which can never fire and looks entirely reasonable in the table. And nobody wants to be told their first-call rate went *up*.

## Every rule has a noise floor

`min_calls`, defaulting to **20**.

Two empties out of three calls is 67% and means nothing. Publishing it teaches people to distrust every alert after it, which costs more than the alert was worth.

## Rules are named

Asked for at creation, and required. A list identified only by `first_call_rate < 0.6` tells you the condition and never the intent.

## What fires is frozen

When a rule trips, the finding copies the metric, comparator, scope and rule name **as they were at that moment**. A rule can be renamed or deleted afterwards, and a notification that cannot say why it was sent is worse than none.

The *wording* is not stored — it is rendered from those fields, so the inbox, an email and the MCP server read the same sentence. Prose stored in a database is a second copy of it.

## Evaluation

**02:15 UTC**, fifteen minutes after the [nightly pass](/api/concepts/nightly-pass).

Not beside it: `first_call_rate` is *written* by that pass, so a rule evaluated in the same minute would read a half-computed rate — firing on a figure that was never true, or missing one that was.

Idempotent in both directions. Re-running a day cannot produce a second copy of the same finding, which is what makes recovering a missed night a loop rather than a cleanup.

<Note>
  **Toggling a rule off is a one-field call.** `PATCH /v1/alerts/:id` leaves omitted fields alone, so two people silencing rules at once cannot clobber each other's thresholds.
</Note>

## History

`/m/:id/alerts` also shows what fired: the day, the observed value, the threshold it crossed, and how many calls it was measured over.

## Alerts are a paid feature

Creating a rule, switching an existing one back on, and subscribing to the [weekly digest](/alerts/digest) all require Pro or Scale. Disabling and unsubscribing stay open on any plan — somebody whose subscription ended must be able to tidy up what they can no longer run — and the nightly evaluation skips free accounts, so a rule that outlives its plan does not keep filling an inbox.

## Related

* [Channels](/alerts/channels) — where a firing is delivered
* [The weekly digest](/alerts/digest)
* [Insights](/insights/overview) — the same findings, computed on read rather than fired at you
