Part 4 of 4 for #159 — req 5 of the issue's security requirements (defense-in-depth). Does not block the grant shipping (part 3), but gates closing #159.
Scope
Rate-limit client_credentials token issuance. The plugin has no rate-limiting machinery today, so this is new infrastructure — keep it minimal:
- Per-node, in-memory token bucket keyed by
client_id (documented as per-node: Harper replication makes a shared counter table a hot-write anti-pattern, and the assertion replay guard + 60s exp already bound abuse).
- Configurable limit (e.g.
mcp.clientCredentials.rateLimit requests/min, sensible default), 429 + error: "slow_down"-style OAuth error on exceed.
- Scope decision to settle in this issue: client_credentials-only, or also cover the existing authorization_code/refresh grants while we're here.
Tests
Under-limit requests pass; over-limit returns 429 with an OAuth error body; bucket refills; per-client isolation (one noisy client doesn't starve another).
Dependencies
Part 3 (#162 — the grant must exist to be limited).
Part 4 of 4 for #159 — req 5 of the issue's security requirements (defense-in-depth). Does not block the grant shipping (part 3), but gates closing #159.
Scope
Rate-limit
client_credentialstoken issuance. The plugin has no rate-limiting machinery today, so this is new infrastructure — keep it minimal:client_id(documented as per-node: Harper replication makes a shared counter table a hot-write anti-pattern, and the assertion replay guard + 60sexpalready bound abuse).mcp.clientCredentials.rateLimitrequests/min, sensible default), 429 +error: "slow_down"-style OAuth error on exceed.Tests
Under-limit requests pass; over-limit returns 429 with an OAuth error body; bucket refills; per-client isolation (one noisy client doesn't starve another).
Dependencies
Part 3 (#162 — the grant must exist to be limited).