Skip to main content

Rate-Limit MCP Tools

Tasks

Add call-rate limits to an Access Binding — "100 calls per hour per agent to merge_pull_request" — counted exactly across every AIControls replica. For where limits sit in the access model, see Team Access.

Add a rate limit​

  1. Go to Setup → Settings → Access Bindings and edit the binding whose tools you want to cap.
  2. Under Advanced → Rate limits, add a row:

    FieldValuesMeaning
    Callsany positive numberCalls allowed per window
    Windowper minute, per hour, per dayFixed windows — the count resets at each boundary
    For eachagent, user, team, label value, everyone combinedWho gets their own bucket. everyone combined is one shared bucket — a server-capacity cap
    On exceedDeny, Warn, Require approvalDeny rejects the call; Warn lets it through and raises a notification; Require approval parks each over-limit call in the Approvals queue
  3. Save. The limit applies to the calls the binding covers — its tool rows, or the whole bound server when the binding has none.
note

A limit only counts callers the binding's teams and subjects match — it is a cap on granted traffic, not a global throttle. For one shared cap across all callers, use everyone combined.

What happens on exceed​

  • Deny — the call is rejected with HTTP 429 and the limit's numbers in the message. Security Events records a denial with policy binding-rate-limit:<binding>, so limit trips are searchable like any other decision.
  • Warn — the call proceeds; a rate_limit_exceeded notification is dispatched to your configured channels (Slack, Teams, email, in-app).
  • Require approval ("approve to burst") — each over-limit call parks in the Approvals queue for up to two minutes by default. An approval releases that one call; on timeout the call is denied by default. Useful when the cap is a guardrail rather than a hard ceiling — a reviewer can wave through a legitimate burst without editing the binding. Both the wait time and the timeout outcome are configurable per rate limit — see below.

Either way the notification includes the agent, tool, and configured limit.

Configuring the "require approval" timeout​

A require-approval limit accepts two optional overrides, set alongside the limit itself:

FieldDefaultMeaning
Approval timeout (minutes)2How long an over-limit call waits in the Approvals queue before timing out
On timeoutDenyWhat happens if nobody reviews it in time: Deny, Warn (let it through, notify), or Allow (let it through silently) — timeoutAction in the API, lowercase (deny/warn/allow)

Leave both blank to keep the defaults — a short interactive window with a deny fallback suits a developer waiting on their own call. Widen the timeout (or switch the timeout action to Warn/Allow) for limits an on-call approver reviews rather than the calling developer, where a two-minute window is unrealistic.

Verify​

  1. Set a deliberately small limit (e.g. 2 per minute per agent) on a test binding.
  2. Call the covered tool three times as the same agent — the third call fails with HTTP 429.
  3. Find the denial in Security Events by policy name binding-rate-limit:, then restore the real limit.

Notes on counting​

  • Counts are stored centrally, so limits hold exactly even with multiple AIControls replicas — two replicas cannot each allow the same "last" call.
  • Windows are fixed, not sliding: a per hour limit resets on the hour boundary.
  • If the counter store is briefly unreachable, calls are allowed rather than blocked (fail-open) and the condition is logged.

This is a different limiter than the global RPM/TPM protection​

Access-Binding rate limits (this page) are a separate mechanism from the Requests per minute / Tokens per minute global defaults and per-group/per-identity overrides under Settings → Advanced → Rate Limits. That RPM/TPM protection is a coarser, per-agent request-rate safety net rather than a per-tool cap, and its fleet-wide accuracy is weaker than the exact counting above:

  • It's enforced per replica, in memory — there's no shared counter. Under multiple replicas, each pod's share of the configured limit is set by dividing across the fleet's expected replica count (self-hosted only — see PostgreSQL Backend: HA with multiple replicas).
  • With more than one expected replica, the global default RPM tier additionally gets a periodic (roughly every 5 seconds) fleet-wide correction on top of that split, so sustained overshoot self-corrects within a few seconds. A single replica doesn't need it: its own limit, including the burst size, is already exact. The fleet count runs per minute: if you lower the global RPM limit mid-minute, requests already admitted that minute count toward the new limit.
  • Per-group and per-identity overrides get no live correction — they rely solely on the static per-replica split, so they can run persistently above or below the configured number if traffic isn't evenly spread across replicas.

If you need an exact, per-tool cap, use an Access Binding rate limit as described above rather than the global RPM/TPM setting.

Hitting a Tokens per minute limit fires a rate_limit_exceeded notification to your configured external channels (Slack, Teams, email, webhook). It does not go to the in-app inbox. It is cooled down to at most once per agent every 5 minutes, whether the request that tripped it was an LLM call or an MCP tool call.

Upgrading token-derived identity overrides​

Anonymous token identities now use the complete SHA-256 digest after anon: or opaque:. If a per-identity RPM or TPM override uses an older eight-character digest, replace its key with the corresponding full identity in Settings → Advanced → Rate Limits before admitting traffic for that identity. Update any policy or budget keyed to the old identifier as well. Use the full identity observed for the caller; do not copy one old override to every identity sharing its short prefix.

Old keys no longer match, so group or global defaults apply until updated. A compatibility alias would let colliding credentials share restrictions or permissions. Historical activity retains its original identifiers. Named users and service accounts keep their existing identifiers.

See also​