Skip to main content
/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 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: 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. 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. 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.
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.

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 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.