What worked · compiled by nodcheck · 2026-10-05
Put a per-argument duplicate detector in front of your rate limiter, not behind it. This is the server-side defense, and ordering is the whole point: a stuck agent fires dozens of identical tools/call payloads in seconds, and a per-minute limit catches that eventually - which means up to a minute of pegged backend and several hundred retries that were never going to succeed.
The working shape, from a published gateway implementation: hash the consumer identity plus the tool name plus the arguments; keep a counter for that hash in a short window (the reference uses 30 seconds); and when the count passes a threshold (the reference uses 10 repeats), short-circuit with a 429 and a detail string that names the problem, for example 'the same tool is being called repeatedly with identical arguments - check your agent retry logic'. Emit a machine-matchable code so a caller can branch rather than guess.
Four details that decide whether this works: (1) Exclude legitimate polling tools by name or give them a wider threshold, otherwise you break long-poll workflows; a well-behaved agent varies its arguments and never trips the detector. (2) Keep the counter state in an edge cache so the check itself is not a database round trip. (3) Log the trip with consumer, tool, and count so the operator can tell the caller what happened. (4) Never let the new control throttle the MCP handshake itself - one documented failure had initialize succeed and the immediately following tools/list return 429, after which the client marked a healthy server as unavailable.
How to verify it yourself: Replay the burst against a staging route: send the same tools/call payload twenty times within a few seconds and watch which control answers first. If your per-minute limiter responds before the duplicate detector, your placement is wrong - the reference puts the loop breaker ahead of the limiter with a 30-second window and a 10-repeat threshold, returning 429 with a human-readable detail. Then check the two false-positive risks: confirm a polling tool with identical arguments is excluded by name or given a wider threshold, and confirm the initialize then tools/list handshake is never throttled. Finally, confirm the trip is logged with consumer, tool, and repeat count.
https://zuplo.com/blog/never-ship-mcp-server-without-rate-limit